162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * virtio-snd: Virtio sound device
462306a36Sopenharmony_ci * Copyright (C) 2021 OpenSynergy GmbH
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include <linux/moduleparam.h>
762306a36Sopenharmony_ci#include <linux/virtio_config.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "virtio_card.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistatic u32 pcm_buffer_ms = 160;
1262306a36Sopenharmony_cimodule_param(pcm_buffer_ms, uint, 0644);
1362306a36Sopenharmony_ciMODULE_PARM_DESC(pcm_buffer_ms, "PCM substream buffer time in milliseconds");
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistatic u32 pcm_periods_min = 2;
1662306a36Sopenharmony_cimodule_param(pcm_periods_min, uint, 0644);
1762306a36Sopenharmony_ciMODULE_PARM_DESC(pcm_periods_min, "Minimum number of PCM periods");
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic u32 pcm_periods_max = 16;
2062306a36Sopenharmony_cimodule_param(pcm_periods_max, uint, 0644);
2162306a36Sopenharmony_ciMODULE_PARM_DESC(pcm_periods_max, "Maximum number of PCM periods");
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic u32 pcm_period_ms_min = 10;
2462306a36Sopenharmony_cimodule_param(pcm_period_ms_min, uint, 0644);
2562306a36Sopenharmony_ciMODULE_PARM_DESC(pcm_period_ms_min, "Minimum PCM period time in milliseconds");
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic u32 pcm_period_ms_max = 80;
2862306a36Sopenharmony_cimodule_param(pcm_period_ms_max, uint, 0644);
2962306a36Sopenharmony_ciMODULE_PARM_DESC(pcm_period_ms_max, "Maximum PCM period time in milliseconds");
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/* Map for converting VirtIO format to ALSA format. */
3262306a36Sopenharmony_cistatic const snd_pcm_format_t g_v2a_format_map[] = {
3362306a36Sopenharmony_ci	[VIRTIO_SND_PCM_FMT_IMA_ADPCM] = SNDRV_PCM_FORMAT_IMA_ADPCM,
3462306a36Sopenharmony_ci	[VIRTIO_SND_PCM_FMT_MU_LAW] = SNDRV_PCM_FORMAT_MU_LAW,
3562306a36Sopenharmony_ci	[VIRTIO_SND_PCM_FMT_A_LAW] = SNDRV_PCM_FORMAT_A_LAW,
3662306a36Sopenharmony_ci	[VIRTIO_SND_PCM_FMT_S8] = SNDRV_PCM_FORMAT_S8,
3762306a36Sopenharmony_ci	[VIRTIO_SND_PCM_FMT_U8] = SNDRV_PCM_FORMAT_U8,
3862306a36Sopenharmony_ci	[VIRTIO_SND_PCM_FMT_S16] = SNDRV_PCM_FORMAT_S16_LE,
3962306a36Sopenharmony_ci	[VIRTIO_SND_PCM_FMT_U16] = SNDRV_PCM_FORMAT_U16_LE,
4062306a36Sopenharmony_ci	[VIRTIO_SND_PCM_FMT_S18_3] = SNDRV_PCM_FORMAT_S18_3LE,
4162306a36Sopenharmony_ci	[VIRTIO_SND_PCM_FMT_U18_3] = SNDRV_PCM_FORMAT_U18_3LE,
4262306a36Sopenharmony_ci	[VIRTIO_SND_PCM_FMT_S20_3] = SNDRV_PCM_FORMAT_S20_3LE,
4362306a36Sopenharmony_ci	[VIRTIO_SND_PCM_FMT_U20_3] = SNDRV_PCM_FORMAT_U20_3LE,
4462306a36Sopenharmony_ci	[VIRTIO_SND_PCM_FMT_S24_3] = SNDRV_PCM_FORMAT_S24_3LE,
4562306a36Sopenharmony_ci	[VIRTIO_SND_PCM_FMT_U24_3] = SNDRV_PCM_FORMAT_U24_3LE,
4662306a36Sopenharmony_ci	[VIRTIO_SND_PCM_FMT_S20] = SNDRV_PCM_FORMAT_S20_LE,
4762306a36Sopenharmony_ci	[VIRTIO_SND_PCM_FMT_U20] = SNDRV_PCM_FORMAT_U20_LE,
4862306a36Sopenharmony_ci	[VIRTIO_SND_PCM_FMT_S24] = SNDRV_PCM_FORMAT_S24_LE,
4962306a36Sopenharmony_ci	[VIRTIO_SND_PCM_FMT_U24] = SNDRV_PCM_FORMAT_U24_LE,
5062306a36Sopenharmony_ci	[VIRTIO_SND_PCM_FMT_S32] = SNDRV_PCM_FORMAT_S32_LE,
5162306a36Sopenharmony_ci	[VIRTIO_SND_PCM_FMT_U32] = SNDRV_PCM_FORMAT_U32_LE,
5262306a36Sopenharmony_ci	[VIRTIO_SND_PCM_FMT_FLOAT] = SNDRV_PCM_FORMAT_FLOAT_LE,
5362306a36Sopenharmony_ci	[VIRTIO_SND_PCM_FMT_FLOAT64] = SNDRV_PCM_FORMAT_FLOAT64_LE,
5462306a36Sopenharmony_ci	[VIRTIO_SND_PCM_FMT_DSD_U8] = SNDRV_PCM_FORMAT_DSD_U8,
5562306a36Sopenharmony_ci	[VIRTIO_SND_PCM_FMT_DSD_U16] = SNDRV_PCM_FORMAT_DSD_U16_LE,
5662306a36Sopenharmony_ci	[VIRTIO_SND_PCM_FMT_DSD_U32] = SNDRV_PCM_FORMAT_DSD_U32_LE,
5762306a36Sopenharmony_ci	[VIRTIO_SND_PCM_FMT_IEC958_SUBFRAME] =
5862306a36Sopenharmony_ci		SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/* Map for converting VirtIO frame rate to ALSA frame rate. */
6262306a36Sopenharmony_cistruct virtsnd_v2a_rate {
6362306a36Sopenharmony_ci	unsigned int alsa_bit;
6462306a36Sopenharmony_ci	unsigned int rate;
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic const struct virtsnd_v2a_rate g_v2a_rate_map[] = {
6862306a36Sopenharmony_ci	[VIRTIO_SND_PCM_RATE_5512] = { SNDRV_PCM_RATE_5512, 5512 },
6962306a36Sopenharmony_ci	[VIRTIO_SND_PCM_RATE_8000] = { SNDRV_PCM_RATE_8000, 8000 },
7062306a36Sopenharmony_ci	[VIRTIO_SND_PCM_RATE_11025] = { SNDRV_PCM_RATE_11025, 11025 },
7162306a36Sopenharmony_ci	[VIRTIO_SND_PCM_RATE_16000] = { SNDRV_PCM_RATE_16000, 16000 },
7262306a36Sopenharmony_ci	[VIRTIO_SND_PCM_RATE_22050] = { SNDRV_PCM_RATE_22050, 22050 },
7362306a36Sopenharmony_ci	[VIRTIO_SND_PCM_RATE_32000] = { SNDRV_PCM_RATE_32000, 32000 },
7462306a36Sopenharmony_ci	[VIRTIO_SND_PCM_RATE_44100] = { SNDRV_PCM_RATE_44100, 44100 },
7562306a36Sopenharmony_ci	[VIRTIO_SND_PCM_RATE_48000] = { SNDRV_PCM_RATE_48000, 48000 },
7662306a36Sopenharmony_ci	[VIRTIO_SND_PCM_RATE_64000] = { SNDRV_PCM_RATE_64000, 64000 },
7762306a36Sopenharmony_ci	[VIRTIO_SND_PCM_RATE_88200] = { SNDRV_PCM_RATE_88200, 88200 },
7862306a36Sopenharmony_ci	[VIRTIO_SND_PCM_RATE_96000] = { SNDRV_PCM_RATE_96000, 96000 },
7962306a36Sopenharmony_ci	[VIRTIO_SND_PCM_RATE_176400] = { SNDRV_PCM_RATE_176400, 176400 },
8062306a36Sopenharmony_ci	[VIRTIO_SND_PCM_RATE_192000] = { SNDRV_PCM_RATE_192000, 192000 }
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/**
8462306a36Sopenharmony_ci * virtsnd_pcm_build_hw() - Parse substream config and build HW descriptor.
8562306a36Sopenharmony_ci * @vss: VirtIO substream.
8662306a36Sopenharmony_ci * @info: VirtIO substream information entry.
8762306a36Sopenharmony_ci *
8862306a36Sopenharmony_ci * Context: Any context.
8962306a36Sopenharmony_ci * Return: 0 on success, -EINVAL if configuration is invalid.
9062306a36Sopenharmony_ci */
9162306a36Sopenharmony_cistatic int virtsnd_pcm_build_hw(struct virtio_pcm_substream *vss,
9262306a36Sopenharmony_ci				struct virtio_snd_pcm_info *info)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	struct virtio_device *vdev = vss->snd->vdev;
9562306a36Sopenharmony_ci	unsigned int i;
9662306a36Sopenharmony_ci	u64 values;
9762306a36Sopenharmony_ci	size_t sample_max = 0;
9862306a36Sopenharmony_ci	size_t sample_min = 0;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	vss->features = le32_to_cpu(info->features);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	/*
10362306a36Sopenharmony_ci	 * TODO: set SNDRV_PCM_INFO_{BATCH,BLOCK_TRANSFER} if device supports
10462306a36Sopenharmony_ci	 * only message-based transport.
10562306a36Sopenharmony_ci	 */
10662306a36Sopenharmony_ci	vss->hw.info =
10762306a36Sopenharmony_ci		SNDRV_PCM_INFO_MMAP |
10862306a36Sopenharmony_ci		SNDRV_PCM_INFO_MMAP_VALID |
10962306a36Sopenharmony_ci		SNDRV_PCM_INFO_BATCH |
11062306a36Sopenharmony_ci		SNDRV_PCM_INFO_BLOCK_TRANSFER |
11162306a36Sopenharmony_ci		SNDRV_PCM_INFO_INTERLEAVED |
11262306a36Sopenharmony_ci		SNDRV_PCM_INFO_PAUSE;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	if (!info->channels_min || info->channels_min > info->channels_max) {
11562306a36Sopenharmony_ci		dev_err(&vdev->dev,
11662306a36Sopenharmony_ci			"SID %u: invalid channel range [%u %u]\n",
11762306a36Sopenharmony_ci			vss->sid, info->channels_min, info->channels_max);
11862306a36Sopenharmony_ci		return -EINVAL;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	vss->hw.channels_min = info->channels_min;
12262306a36Sopenharmony_ci	vss->hw.channels_max = info->channels_max;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	values = le64_to_cpu(info->formats);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	vss->hw.formats = 0;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(g_v2a_format_map); ++i)
12962306a36Sopenharmony_ci		if (values & (1ULL << i)) {
13062306a36Sopenharmony_ci			snd_pcm_format_t alsa_fmt = g_v2a_format_map[i];
13162306a36Sopenharmony_ci			int bytes = snd_pcm_format_physical_width(alsa_fmt) / 8;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci			if (!sample_min || sample_min > bytes)
13462306a36Sopenharmony_ci				sample_min = bytes;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci			if (sample_max < bytes)
13762306a36Sopenharmony_ci				sample_max = bytes;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci			vss->hw.formats |= pcm_format_to_bits(alsa_fmt);
14062306a36Sopenharmony_ci		}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if (!vss->hw.formats) {
14362306a36Sopenharmony_ci		dev_err(&vdev->dev,
14462306a36Sopenharmony_ci			"SID %u: no supported PCM sample formats found\n",
14562306a36Sopenharmony_ci			vss->sid);
14662306a36Sopenharmony_ci		return -EINVAL;
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	values = le64_to_cpu(info->rates);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	vss->hw.rates = 0;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(g_v2a_rate_map); ++i)
15462306a36Sopenharmony_ci		if (values & (1ULL << i)) {
15562306a36Sopenharmony_ci			if (!vss->hw.rate_min ||
15662306a36Sopenharmony_ci			    vss->hw.rate_min > g_v2a_rate_map[i].rate)
15762306a36Sopenharmony_ci				vss->hw.rate_min = g_v2a_rate_map[i].rate;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci			if (vss->hw.rate_max < g_v2a_rate_map[i].rate)
16062306a36Sopenharmony_ci				vss->hw.rate_max = g_v2a_rate_map[i].rate;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci			vss->hw.rates |= g_v2a_rate_map[i].alsa_bit;
16362306a36Sopenharmony_ci		}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (!vss->hw.rates) {
16662306a36Sopenharmony_ci		dev_err(&vdev->dev,
16762306a36Sopenharmony_ci			"SID %u: no supported PCM frame rates found\n",
16862306a36Sopenharmony_ci			vss->sid);
16962306a36Sopenharmony_ci		return -EINVAL;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	vss->hw.periods_min = pcm_periods_min;
17362306a36Sopenharmony_ci	vss->hw.periods_max = pcm_periods_max;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	/*
17662306a36Sopenharmony_ci	 * We must ensure that there is enough space in the buffer to store
17762306a36Sopenharmony_ci	 * pcm_buffer_ms ms for the combination (Cmax, Smax, Rmax), where:
17862306a36Sopenharmony_ci	 *   Cmax = maximum supported number of channels,
17962306a36Sopenharmony_ci	 *   Smax = maximum supported sample size in bytes,
18062306a36Sopenharmony_ci	 *   Rmax = maximum supported frame rate.
18162306a36Sopenharmony_ci	 */
18262306a36Sopenharmony_ci	vss->hw.buffer_bytes_max =
18362306a36Sopenharmony_ci		PAGE_ALIGN(sample_max * vss->hw.channels_max * pcm_buffer_ms *
18462306a36Sopenharmony_ci			   (vss->hw.rate_max / MSEC_PER_SEC));
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	/*
18762306a36Sopenharmony_ci	 * We must ensure that the minimum period size is enough to store
18862306a36Sopenharmony_ci	 * pcm_period_ms_min ms for the combination (Cmin, Smin, Rmin), where:
18962306a36Sopenharmony_ci	 *   Cmin = minimum supported number of channels,
19062306a36Sopenharmony_ci	 *   Smin = minimum supported sample size in bytes,
19162306a36Sopenharmony_ci	 *   Rmin = minimum supported frame rate.
19262306a36Sopenharmony_ci	 */
19362306a36Sopenharmony_ci	vss->hw.period_bytes_min =
19462306a36Sopenharmony_ci		sample_min * vss->hw.channels_min * pcm_period_ms_min *
19562306a36Sopenharmony_ci		(vss->hw.rate_min / MSEC_PER_SEC);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	/*
19862306a36Sopenharmony_ci	 * We must ensure that the maximum period size is enough to store
19962306a36Sopenharmony_ci	 * pcm_period_ms_max ms for the combination (Cmax, Smax, Rmax).
20062306a36Sopenharmony_ci	 */
20162306a36Sopenharmony_ci	vss->hw.period_bytes_max =
20262306a36Sopenharmony_ci		sample_max * vss->hw.channels_max * pcm_period_ms_max *
20362306a36Sopenharmony_ci		(vss->hw.rate_max / MSEC_PER_SEC);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	return 0;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci/**
20962306a36Sopenharmony_ci * virtsnd_pcm_find() - Find the PCM device for the specified node ID.
21062306a36Sopenharmony_ci * @snd: VirtIO sound device.
21162306a36Sopenharmony_ci * @nid: Function node ID.
21262306a36Sopenharmony_ci *
21362306a36Sopenharmony_ci * Context: Any context.
21462306a36Sopenharmony_ci * Return: a pointer to the PCM device or ERR_PTR(-ENOENT).
21562306a36Sopenharmony_ci */
21662306a36Sopenharmony_cistruct virtio_pcm *virtsnd_pcm_find(struct virtio_snd *snd, u32 nid)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	struct virtio_pcm *vpcm;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	list_for_each_entry(vpcm, &snd->pcm_list, list)
22162306a36Sopenharmony_ci		if (vpcm->nid == nid)
22262306a36Sopenharmony_ci			return vpcm;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	return ERR_PTR(-ENOENT);
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci/**
22862306a36Sopenharmony_ci * virtsnd_pcm_find_or_create() - Find or create the PCM device for the
22962306a36Sopenharmony_ci *                                specified node ID.
23062306a36Sopenharmony_ci * @snd: VirtIO sound device.
23162306a36Sopenharmony_ci * @nid: Function node ID.
23262306a36Sopenharmony_ci *
23362306a36Sopenharmony_ci * Context: Any context that permits to sleep.
23462306a36Sopenharmony_ci * Return: a pointer to the PCM device or ERR_PTR(-errno).
23562306a36Sopenharmony_ci */
23662306a36Sopenharmony_cistruct virtio_pcm *virtsnd_pcm_find_or_create(struct virtio_snd *snd, u32 nid)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	struct virtio_device *vdev = snd->vdev;
23962306a36Sopenharmony_ci	struct virtio_pcm *vpcm;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	vpcm = virtsnd_pcm_find(snd, nid);
24262306a36Sopenharmony_ci	if (!IS_ERR(vpcm))
24362306a36Sopenharmony_ci		return vpcm;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	vpcm = devm_kzalloc(&vdev->dev, sizeof(*vpcm), GFP_KERNEL);
24662306a36Sopenharmony_ci	if (!vpcm)
24762306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	vpcm->nid = nid;
25062306a36Sopenharmony_ci	list_add_tail(&vpcm->list, &snd->pcm_list);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	return vpcm;
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci/**
25662306a36Sopenharmony_ci * virtsnd_pcm_validate() - Validate if the device can be started.
25762306a36Sopenharmony_ci * @vdev: VirtIO parent device.
25862306a36Sopenharmony_ci *
25962306a36Sopenharmony_ci * Context: Any context.
26062306a36Sopenharmony_ci * Return: 0 on success, -EINVAL on failure.
26162306a36Sopenharmony_ci */
26262306a36Sopenharmony_ciint virtsnd_pcm_validate(struct virtio_device *vdev)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	if (pcm_periods_min < 2 || pcm_periods_min > pcm_periods_max) {
26562306a36Sopenharmony_ci		dev_err(&vdev->dev,
26662306a36Sopenharmony_ci			"invalid range [%u %u] of the number of PCM periods\n",
26762306a36Sopenharmony_ci			pcm_periods_min, pcm_periods_max);
26862306a36Sopenharmony_ci		return -EINVAL;
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	if (!pcm_period_ms_min || pcm_period_ms_min > pcm_period_ms_max) {
27262306a36Sopenharmony_ci		dev_err(&vdev->dev,
27362306a36Sopenharmony_ci			"invalid range [%u %u] of the size of the PCM period\n",
27462306a36Sopenharmony_ci			pcm_period_ms_min, pcm_period_ms_max);
27562306a36Sopenharmony_ci		return -EINVAL;
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (pcm_buffer_ms < pcm_periods_min * pcm_period_ms_min) {
27962306a36Sopenharmony_ci		dev_err(&vdev->dev,
28062306a36Sopenharmony_ci			"pcm_buffer_ms(=%u) value cannot be < %u ms\n",
28162306a36Sopenharmony_ci			pcm_buffer_ms, pcm_periods_min * pcm_period_ms_min);
28262306a36Sopenharmony_ci		return -EINVAL;
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	if (pcm_period_ms_max > pcm_buffer_ms / 2) {
28662306a36Sopenharmony_ci		dev_err(&vdev->dev,
28762306a36Sopenharmony_ci			"pcm_period_ms_max(=%u) value cannot be > %u ms\n",
28862306a36Sopenharmony_ci			pcm_period_ms_max, pcm_buffer_ms / 2);
28962306a36Sopenharmony_ci		return -EINVAL;
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	return 0;
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci/**
29662306a36Sopenharmony_ci * virtsnd_pcm_period_elapsed() - Kernel work function to handle the elapsed
29762306a36Sopenharmony_ci *                                period state.
29862306a36Sopenharmony_ci * @work: Elapsed period work.
29962306a36Sopenharmony_ci *
30062306a36Sopenharmony_ci * The main purpose of this function is to call snd_pcm_period_elapsed() in
30162306a36Sopenharmony_ci * a process context, not in an interrupt context. This is necessary because PCM
30262306a36Sopenharmony_ci * devices operate in non-atomic mode.
30362306a36Sopenharmony_ci *
30462306a36Sopenharmony_ci * Context: Process context.
30562306a36Sopenharmony_ci */
30662306a36Sopenharmony_cistatic void virtsnd_pcm_period_elapsed(struct work_struct *work)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	struct virtio_pcm_substream *vss =
30962306a36Sopenharmony_ci		container_of(work, struct virtio_pcm_substream, elapsed_period);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	snd_pcm_period_elapsed(vss->substream);
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci/**
31562306a36Sopenharmony_ci * virtsnd_pcm_parse_cfg() - Parse the stream configuration.
31662306a36Sopenharmony_ci * @snd: VirtIO sound device.
31762306a36Sopenharmony_ci *
31862306a36Sopenharmony_ci * This function is called during initial device initialization.
31962306a36Sopenharmony_ci *
32062306a36Sopenharmony_ci * Context: Any context that permits to sleep.
32162306a36Sopenharmony_ci * Return: 0 on success, -errno on failure.
32262306a36Sopenharmony_ci */
32362306a36Sopenharmony_ciint virtsnd_pcm_parse_cfg(struct virtio_snd *snd)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	struct virtio_device *vdev = snd->vdev;
32662306a36Sopenharmony_ci	struct virtio_snd_pcm_info *info;
32762306a36Sopenharmony_ci	u32 i;
32862306a36Sopenharmony_ci	int rc;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	virtio_cread_le(vdev, struct virtio_snd_config, streams,
33162306a36Sopenharmony_ci			&snd->nsubstreams);
33262306a36Sopenharmony_ci	if (!snd->nsubstreams)
33362306a36Sopenharmony_ci		return 0;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	snd->substreams = devm_kcalloc(&vdev->dev, snd->nsubstreams,
33662306a36Sopenharmony_ci				       sizeof(*snd->substreams), GFP_KERNEL);
33762306a36Sopenharmony_ci	if (!snd->substreams)
33862306a36Sopenharmony_ci		return -ENOMEM;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	info = kcalloc(snd->nsubstreams, sizeof(*info), GFP_KERNEL);
34162306a36Sopenharmony_ci	if (!info)
34262306a36Sopenharmony_ci		return -ENOMEM;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	rc = virtsnd_ctl_query_info(snd, VIRTIO_SND_R_PCM_INFO, 0,
34562306a36Sopenharmony_ci				    snd->nsubstreams, sizeof(*info), info);
34662306a36Sopenharmony_ci	if (rc)
34762306a36Sopenharmony_ci		goto on_exit;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	for (i = 0; i < snd->nsubstreams; ++i) {
35062306a36Sopenharmony_ci		struct virtio_pcm_substream *vss = &snd->substreams[i];
35162306a36Sopenharmony_ci		struct virtio_pcm *vpcm;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci		vss->snd = snd;
35462306a36Sopenharmony_ci		vss->sid = i;
35562306a36Sopenharmony_ci		INIT_WORK(&vss->elapsed_period, virtsnd_pcm_period_elapsed);
35662306a36Sopenharmony_ci		init_waitqueue_head(&vss->msg_empty);
35762306a36Sopenharmony_ci		spin_lock_init(&vss->lock);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci		rc = virtsnd_pcm_build_hw(vss, &info[i]);
36062306a36Sopenharmony_ci		if (rc)
36162306a36Sopenharmony_ci			goto on_exit;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci		vss->nid = le32_to_cpu(info[i].hdr.hda_fn_nid);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci		vpcm = virtsnd_pcm_find_or_create(snd, vss->nid);
36662306a36Sopenharmony_ci		if (IS_ERR(vpcm)) {
36762306a36Sopenharmony_ci			rc = PTR_ERR(vpcm);
36862306a36Sopenharmony_ci			goto on_exit;
36962306a36Sopenharmony_ci		}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci		switch (info[i].direction) {
37262306a36Sopenharmony_ci		case VIRTIO_SND_D_OUTPUT:
37362306a36Sopenharmony_ci			vss->direction = SNDRV_PCM_STREAM_PLAYBACK;
37462306a36Sopenharmony_ci			break;
37562306a36Sopenharmony_ci		case VIRTIO_SND_D_INPUT:
37662306a36Sopenharmony_ci			vss->direction = SNDRV_PCM_STREAM_CAPTURE;
37762306a36Sopenharmony_ci			break;
37862306a36Sopenharmony_ci		default:
37962306a36Sopenharmony_ci			dev_err(&vdev->dev, "SID %u: unknown direction (%u)\n",
38062306a36Sopenharmony_ci				vss->sid, info[i].direction);
38162306a36Sopenharmony_ci			rc = -EINVAL;
38262306a36Sopenharmony_ci			goto on_exit;
38362306a36Sopenharmony_ci		}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci		vpcm->streams[vss->direction].nsubstreams++;
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cion_exit:
38962306a36Sopenharmony_ci	kfree(info);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	return rc;
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci/**
39562306a36Sopenharmony_ci * virtsnd_pcm_build_devs() - Build ALSA PCM devices.
39662306a36Sopenharmony_ci * @snd: VirtIO sound device.
39762306a36Sopenharmony_ci *
39862306a36Sopenharmony_ci * Context: Any context that permits to sleep.
39962306a36Sopenharmony_ci * Return: 0 on success, -errno on failure.
40062306a36Sopenharmony_ci */
40162306a36Sopenharmony_ciint virtsnd_pcm_build_devs(struct virtio_snd *snd)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	struct virtio_device *vdev = snd->vdev;
40462306a36Sopenharmony_ci	struct virtio_pcm *vpcm;
40562306a36Sopenharmony_ci	u32 i;
40662306a36Sopenharmony_ci	int rc;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	list_for_each_entry(vpcm, &snd->pcm_list, list) {
40962306a36Sopenharmony_ci		unsigned int npbs =
41062306a36Sopenharmony_ci			vpcm->streams[SNDRV_PCM_STREAM_PLAYBACK].nsubstreams;
41162306a36Sopenharmony_ci		unsigned int ncps =
41262306a36Sopenharmony_ci			vpcm->streams[SNDRV_PCM_STREAM_CAPTURE].nsubstreams;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci		if (!npbs && !ncps)
41562306a36Sopenharmony_ci			continue;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci		rc = snd_pcm_new(snd->card, VIRTIO_SND_CARD_DRIVER, vpcm->nid,
41862306a36Sopenharmony_ci				 npbs, ncps, &vpcm->pcm);
41962306a36Sopenharmony_ci		if (rc) {
42062306a36Sopenharmony_ci			dev_err(&vdev->dev, "snd_pcm_new[%u] failed: %d\n",
42162306a36Sopenharmony_ci				vpcm->nid, rc);
42262306a36Sopenharmony_ci			return rc;
42362306a36Sopenharmony_ci		}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci		vpcm->pcm->info_flags = 0;
42662306a36Sopenharmony_ci		vpcm->pcm->dev_class = SNDRV_PCM_CLASS_GENERIC;
42762306a36Sopenharmony_ci		vpcm->pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
42862306a36Sopenharmony_ci		snprintf(vpcm->pcm->name, sizeof(vpcm->pcm->name),
42962306a36Sopenharmony_ci			 VIRTIO_SND_PCM_NAME " %u", vpcm->pcm->device);
43062306a36Sopenharmony_ci		vpcm->pcm->private_data = vpcm;
43162306a36Sopenharmony_ci		vpcm->pcm->nonatomic = true;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(vpcm->streams); ++i) {
43462306a36Sopenharmony_ci			struct virtio_pcm_stream *stream = &vpcm->streams[i];
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci			if (!stream->nsubstreams)
43762306a36Sopenharmony_ci				continue;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci			stream->substreams =
44062306a36Sopenharmony_ci				devm_kcalloc(&vdev->dev, stream->nsubstreams,
44162306a36Sopenharmony_ci					     sizeof(*stream->substreams),
44262306a36Sopenharmony_ci					     GFP_KERNEL);
44362306a36Sopenharmony_ci			if (!stream->substreams)
44462306a36Sopenharmony_ci				return -ENOMEM;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci			stream->nsubstreams = 0;
44762306a36Sopenharmony_ci		}
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	for (i = 0; i < snd->nsubstreams; ++i) {
45162306a36Sopenharmony_ci		struct virtio_pcm_stream *vs;
45262306a36Sopenharmony_ci		struct virtio_pcm_substream *vss = &snd->substreams[i];
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci		vpcm = virtsnd_pcm_find(snd, vss->nid);
45562306a36Sopenharmony_ci		if (IS_ERR(vpcm))
45662306a36Sopenharmony_ci			return PTR_ERR(vpcm);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		vs = &vpcm->streams[vss->direction];
45962306a36Sopenharmony_ci		vs->substreams[vs->nsubstreams++] = vss;
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	list_for_each_entry(vpcm, &snd->pcm_list, list) {
46362306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(vpcm->streams); ++i) {
46462306a36Sopenharmony_ci			struct virtio_pcm_stream *vs = &vpcm->streams[i];
46562306a36Sopenharmony_ci			struct snd_pcm_str *ks = &vpcm->pcm->streams[i];
46662306a36Sopenharmony_ci			struct snd_pcm_substream *kss;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci			if (!vs->nsubstreams)
46962306a36Sopenharmony_ci				continue;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci			for (kss = ks->substream; kss; kss = kss->next)
47262306a36Sopenharmony_ci				vs->substreams[kss->number]->substream = kss;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci			snd_pcm_set_ops(vpcm->pcm, i, &virtsnd_pcm_ops);
47562306a36Sopenharmony_ci		}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci		snd_pcm_set_managed_buffer_all(vpcm->pcm,
47862306a36Sopenharmony_ci					       SNDRV_DMA_TYPE_VMALLOC, NULL,
47962306a36Sopenharmony_ci					       0, 0);
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	return 0;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci/**
48662306a36Sopenharmony_ci * virtsnd_pcm_event() - Handle the PCM device event notification.
48762306a36Sopenharmony_ci * @snd: VirtIO sound device.
48862306a36Sopenharmony_ci * @event: VirtIO sound event.
48962306a36Sopenharmony_ci *
49062306a36Sopenharmony_ci * Context: Interrupt context.
49162306a36Sopenharmony_ci */
49262306a36Sopenharmony_civoid virtsnd_pcm_event(struct virtio_snd *snd, struct virtio_snd_event *event)
49362306a36Sopenharmony_ci{
49462306a36Sopenharmony_ci	struct virtio_pcm_substream *vss;
49562306a36Sopenharmony_ci	u32 sid = le32_to_cpu(event->data);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (sid >= snd->nsubstreams)
49862306a36Sopenharmony_ci		return;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	vss = &snd->substreams[sid];
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	switch (le32_to_cpu(event->hdr.code)) {
50362306a36Sopenharmony_ci	case VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED:
50462306a36Sopenharmony_ci		/* TODO: deal with shmem elapsed period */
50562306a36Sopenharmony_ci		break;
50662306a36Sopenharmony_ci	case VIRTIO_SND_EVT_PCM_XRUN:
50762306a36Sopenharmony_ci		spin_lock(&vss->lock);
50862306a36Sopenharmony_ci		if (vss->xfer_enabled)
50962306a36Sopenharmony_ci			vss->xfer_xrun = true;
51062306a36Sopenharmony_ci		spin_unlock(&vss->lock);
51162306a36Sopenharmony_ci		break;
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci}
514