virtio: Backport virtio sound driver.
[AGL/meta-agl.git] / meta-agl-bsp / virtualization-layer / recipes-kernel / linux / linux-yocto / virtio-kmeta / bsp / virtio / virtio-snd / 0008-ALSA-virtio-introduce-PCM-channel-map-support.patch
1 From 861932797d59b807b4fcc8a2e12dafbddd5ca4d9 Mon Sep 17 00:00:00 2001
2 From: Anton Yakovlev <anton.yakovlev@opensynergy.com>
3 Date: Tue, 2 Mar 2021 17:47:08 +0100
4 Subject: [PATCH] ALSA: virtio: introduce PCM channel map support
5
6 Enumerate all available PCM channel maps and create ALSA controls.
7
8 Signed-off-by: Anton Yakovlev <anton.yakovlev@opensynergy.com>
9 Link: https://lore.kernel.org/r/20210302164709.3142702-9-anton.yakovlev@opensynergy.com
10 Signed-off-by: Takashi Iwai <tiwai@suse.de>
11 Signed-off-by: Vasyl Vavrychuk <vasyl.vavrychuk@opensynergy.com>
12 ---
13  sound/virtio/Makefile       |   1 +
14  sound/virtio/virtio_card.c  |  10 ++
15  sound/virtio/virtio_card.h  |   8 ++
16  sound/virtio/virtio_chmap.c | 219 ++++++++++++++++++++++++++++++++++++
17  sound/virtio/virtio_pcm.h   |   4 +
18  5 files changed, 242 insertions(+)
19  create mode 100644 sound/virtio/virtio_chmap.c
20
21 diff --git a/sound/virtio/Makefile b/sound/virtio/Makefile
22 index 09f485291285..2742bddb8874 100644
23 --- a/sound/virtio/Makefile
24 +++ b/sound/virtio/Makefile
25 @@ -4,6 +4,7 @@ obj-$(CONFIG_SND_VIRTIO) += virtio_snd.o
26  
27  virtio_snd-objs := \
28         virtio_card.o \
29 +       virtio_chmap.o \
30         virtio_ctl_msg.o \
31         virtio_jack.o \
32         virtio_pcm.o \
33 diff --git a/sound/virtio/virtio_card.c b/sound/virtio/virtio_card.c
34 index 89bd66c1256e..1c03fcc41c3b 100644
35 --- a/sound/virtio/virtio_card.c
36 +++ b/sound/virtio/virtio_card.c
37 @@ -231,6 +231,10 @@ static int virtsnd_build_devs(struct virtio_snd *snd)
38         if (rc)
39                 return rc;
40  
41 +       rc = virtsnd_chmap_parse_cfg(snd);
42 +       if (rc)
43 +               return rc;
44 +
45         if (snd->njacks) {
46                 rc = virtsnd_jack_build_devs(snd);
47                 if (rc)
48 @@ -243,6 +247,12 @@ static int virtsnd_build_devs(struct virtio_snd *snd)
49                         return rc;
50         }
51  
52 +       if (snd->nchmaps) {
53 +               rc = virtsnd_chmap_build_devs(snd);
54 +               if (rc)
55 +                       return rc;
56 +       }
57 +
58         return snd_card_register(snd->card);
59  }
60  
61 diff --git a/sound/virtio/virtio_card.h b/sound/virtio/virtio_card.h
62 index f154313c79fd..86ef3941895e 100644
63 --- a/sound/virtio/virtio_card.h
64 +++ b/sound/virtio/virtio_card.h
65 @@ -43,6 +43,8 @@ struct virtio_snd_queue {
66   * @njacks: Number of jacks.
67   * @substreams: VirtIO PCM substreams.
68   * @nsubstreams: Number of PCM substreams.
69 + * @chmaps: VirtIO channel maps.
70 + * @nchmaps: Number of channel maps.
71   */
72  struct virtio_snd {
73         struct virtio_device *vdev;
74 @@ -55,6 +57,8 @@ struct virtio_snd {
75         u32 njacks;
76         struct virtio_pcm_substream *substreams;
77         u32 nsubstreams;
78 +       struct virtio_snd_chmap_info *chmaps;
79 +       u32 nchmaps;
80  };
81  
82  /* Message completion timeout in milliseconds (module parameter). */
83 @@ -100,4 +104,8 @@ int virtsnd_jack_build_devs(struct virtio_snd *snd);
84  void virtsnd_jack_event(struct virtio_snd *snd,
85                         struct virtio_snd_event *event);
86  
87 +int virtsnd_chmap_parse_cfg(struct virtio_snd *snd);
88 +
89 +int virtsnd_chmap_build_devs(struct virtio_snd *snd);
90 +
91  #endif /* VIRTIO_SND_CARD_H */
92 diff --git a/sound/virtio/virtio_chmap.c b/sound/virtio/virtio_chmap.c
93 new file mode 100644
94 index 000000000000..5bc924933a59
95 --- /dev/null
96 +++ b/sound/virtio/virtio_chmap.c
97 @@ -0,0 +1,219 @@
98 +// SPDX-License-Identifier: GPL-2.0+
99 +/*
100 + * virtio-snd: Virtio sound device
101 + * Copyright (C) 2021 OpenSynergy GmbH
102 + */
103 +#include <linux/virtio_config.h>
104 +
105 +#include "virtio_card.h"
106 +
107 +/* VirtIO->ALSA channel position map */
108 +static const u8 g_v2a_position_map[] = {
109 +       [VIRTIO_SND_CHMAP_NONE] = SNDRV_CHMAP_UNKNOWN,
110 +       [VIRTIO_SND_CHMAP_NA] = SNDRV_CHMAP_NA,
111 +       [VIRTIO_SND_CHMAP_MONO] = SNDRV_CHMAP_MONO,
112 +       [VIRTIO_SND_CHMAP_FL] = SNDRV_CHMAP_FL,
113 +       [VIRTIO_SND_CHMAP_FR] = SNDRV_CHMAP_FR,
114 +       [VIRTIO_SND_CHMAP_RL] = SNDRV_CHMAP_RL,
115 +       [VIRTIO_SND_CHMAP_RR] = SNDRV_CHMAP_RR,
116 +       [VIRTIO_SND_CHMAP_FC] = SNDRV_CHMAP_FC,
117 +       [VIRTIO_SND_CHMAP_LFE] = SNDRV_CHMAP_LFE,
118 +       [VIRTIO_SND_CHMAP_SL] = SNDRV_CHMAP_SL,
119 +       [VIRTIO_SND_CHMAP_SR] = SNDRV_CHMAP_SR,
120 +       [VIRTIO_SND_CHMAP_RC] = SNDRV_CHMAP_RC,
121 +       [VIRTIO_SND_CHMAP_FLC] = SNDRV_CHMAP_FLC,
122 +       [VIRTIO_SND_CHMAP_FRC] = SNDRV_CHMAP_FRC,
123 +       [VIRTIO_SND_CHMAP_RLC] = SNDRV_CHMAP_RLC,
124 +       [VIRTIO_SND_CHMAP_RRC] = SNDRV_CHMAP_RRC,
125 +       [VIRTIO_SND_CHMAP_FLW] = SNDRV_CHMAP_FLW,
126 +       [VIRTIO_SND_CHMAP_FRW] = SNDRV_CHMAP_FRW,
127 +       [VIRTIO_SND_CHMAP_FLH] = SNDRV_CHMAP_FLH,
128 +       [VIRTIO_SND_CHMAP_FCH] = SNDRV_CHMAP_FCH,
129 +       [VIRTIO_SND_CHMAP_FRH] = SNDRV_CHMAP_FRH,
130 +       [VIRTIO_SND_CHMAP_TC] = SNDRV_CHMAP_TC,
131 +       [VIRTIO_SND_CHMAP_TFL] = SNDRV_CHMAP_TFL,
132 +       [VIRTIO_SND_CHMAP_TFR] = SNDRV_CHMAP_TFR,
133 +       [VIRTIO_SND_CHMAP_TFC] = SNDRV_CHMAP_TFC,
134 +       [VIRTIO_SND_CHMAP_TRL] = SNDRV_CHMAP_TRL,
135 +       [VIRTIO_SND_CHMAP_TRR] = SNDRV_CHMAP_TRR,
136 +       [VIRTIO_SND_CHMAP_TRC] = SNDRV_CHMAP_TRC,
137 +       [VIRTIO_SND_CHMAP_TFLC] = SNDRV_CHMAP_TFLC,
138 +       [VIRTIO_SND_CHMAP_TFRC] = SNDRV_CHMAP_TFRC,
139 +       [VIRTIO_SND_CHMAP_TSL] = SNDRV_CHMAP_TSL,
140 +       [VIRTIO_SND_CHMAP_TSR] = SNDRV_CHMAP_TSR,
141 +       [VIRTIO_SND_CHMAP_LLFE] = SNDRV_CHMAP_LLFE,
142 +       [VIRTIO_SND_CHMAP_RLFE] = SNDRV_CHMAP_RLFE,
143 +       [VIRTIO_SND_CHMAP_BC] = SNDRV_CHMAP_BC,
144 +       [VIRTIO_SND_CHMAP_BLC] = SNDRV_CHMAP_BLC,
145 +       [VIRTIO_SND_CHMAP_BRC] = SNDRV_CHMAP_BRC
146 +};
147 +
148 +/**
149 + * virtsnd_chmap_parse_cfg() - Parse the channel map configuration.
150 + * @snd: VirtIO sound device.
151 + *
152 + * This function is called during initial device initialization.
153 + *
154 + * Context: Any context that permits to sleep.
155 + * Return: 0 on success, -errno on failure.
156 + */
157 +int virtsnd_chmap_parse_cfg(struct virtio_snd *snd)
158 +{
159 +       struct virtio_device *vdev = snd->vdev;
160 +       u32 i;
161 +       int rc;
162 +
163 +       virtio_cread_le(vdev, struct virtio_snd_config, chmaps, &snd->nchmaps);
164 +       if (!snd->nchmaps)
165 +               return 0;
166 +
167 +       snd->chmaps = devm_kcalloc(&vdev->dev, snd->nchmaps,
168 +                                  sizeof(*snd->chmaps), GFP_KERNEL);
169 +       if (!snd->chmaps)
170 +               return -ENOMEM;
171 +
172 +       rc = virtsnd_ctl_query_info(snd, VIRTIO_SND_R_CHMAP_INFO, 0,
173 +                                   snd->nchmaps, sizeof(*snd->chmaps),
174 +                                   snd->chmaps);
175 +       if (rc)
176 +               return rc;
177 +
178 +       /* Count the number of channel maps per each PCM device/stream. */
179 +       for (i = 0; i < snd->nchmaps; ++i) {
180 +               struct virtio_snd_chmap_info *info = &snd->chmaps[i];
181 +               u32 nid = le32_to_cpu(info->hdr.hda_fn_nid);
182 +               struct virtio_pcm *vpcm;
183 +               struct virtio_pcm_stream *vs;
184 +
185 +               vpcm = virtsnd_pcm_find_or_create(snd, nid);
186 +               if (IS_ERR(vpcm))
187 +                       return PTR_ERR(vpcm);
188 +
189 +               switch (info->direction) {
190 +               case VIRTIO_SND_D_OUTPUT:
191 +                       vs = &vpcm->streams[SNDRV_PCM_STREAM_PLAYBACK];
192 +                       break;
193 +               case VIRTIO_SND_D_INPUT:
194 +                       vs = &vpcm->streams[SNDRV_PCM_STREAM_CAPTURE];
195 +                       break;
196 +               default:
197 +                       dev_err(&vdev->dev,
198 +                               "chmap #%u: unknown direction (%u)\n", i,
199 +                               info->direction);
200 +                       return -EINVAL;
201 +               }
202 +
203 +               vs->nchmaps++;
204 +       }
205 +
206 +       return 0;
207 +}
208 +
209 +/**
210 + * virtsnd_chmap_add_ctls() - Create an ALSA control for channel maps.
211 + * @pcm: ALSA PCM device.
212 + * @direction: PCM stream direction (SNDRV_PCM_STREAM_XXX).
213 + * @vs: VirtIO PCM stream.
214 + *
215 + * Context: Any context.
216 + * Return: 0 on success, -errno on failure.
217 + */
218 +static int virtsnd_chmap_add_ctls(struct snd_pcm *pcm, int direction,
219 +                                 struct virtio_pcm_stream *vs)
220 +{
221 +       u32 i;
222 +       int max_channels = 0;
223 +
224 +       for (i = 0; i < vs->nchmaps; i++)
225 +               if (max_channels < vs->chmaps[i].channels)
226 +                       max_channels = vs->chmaps[i].channels;
227 +
228 +       return snd_pcm_add_chmap_ctls(pcm, direction, vs->chmaps, max_channels,
229 +                                     0, NULL);
230 +}
231 +
232 +/**
233 + * virtsnd_chmap_build_devs() - Build ALSA controls for channel maps.
234 + * @snd: VirtIO sound device.
235 + *
236 + * Context: Any context.
237 + * Return: 0 on success, -errno on failure.
238 + */
239 +int virtsnd_chmap_build_devs(struct virtio_snd *snd)
240 +{
241 +       struct virtio_device *vdev = snd->vdev;
242 +       struct virtio_pcm *vpcm;
243 +       struct virtio_pcm_stream *vs;
244 +       u32 i;
245 +       int rc;
246 +
247 +       /* Allocate channel map elements per each PCM device/stream. */
248 +       list_for_each_entry(vpcm, &snd->pcm_list, list) {
249 +               for (i = 0; i < ARRAY_SIZE(vpcm->streams); ++i) {
250 +                       vs = &vpcm->streams[i];
251 +
252 +                       if (!vs->nchmaps)
253 +                               continue;
254 +
255 +                       vs->chmaps = devm_kcalloc(&vdev->dev, vs->nchmaps + 1,
256 +                                                 sizeof(*vs->chmaps),
257 +                                                 GFP_KERNEL);
258 +                       if (!vs->chmaps)
259 +                               return -ENOMEM;
260 +
261 +                       vs->nchmaps = 0;
262 +               }
263 +       }
264 +
265 +       /* Initialize channel maps per each PCM device/stream. */
266 +       for (i = 0; i < snd->nchmaps; ++i) {
267 +               struct virtio_snd_chmap_info *info = &snd->chmaps[i];
268 +               unsigned int channels = info->channels;
269 +               unsigned int ch;
270 +               struct snd_pcm_chmap_elem *chmap;
271 +
272 +               vpcm = virtsnd_pcm_find(snd, le32_to_cpu(info->hdr.hda_fn_nid));
273 +               if (IS_ERR(vpcm))
274 +                       return PTR_ERR(vpcm);
275 +
276 +               if (info->direction == VIRTIO_SND_D_OUTPUT)
277 +                       vs = &vpcm->streams[SNDRV_PCM_STREAM_PLAYBACK];
278 +               else
279 +                       vs = &vpcm->streams[SNDRV_PCM_STREAM_CAPTURE];
280 +
281 +               chmap = &vs->chmaps[vs->nchmaps++];
282 +
283 +               if (channels > ARRAY_SIZE(chmap->map))
284 +                       channels = ARRAY_SIZE(chmap->map);
285 +
286 +               chmap->channels = channels;
287 +
288 +               for (ch = 0; ch < channels; ++ch) {
289 +                       u8 position = info->positions[ch];
290 +
291 +                       if (position >= ARRAY_SIZE(g_v2a_position_map))
292 +                               return -EINVAL;
293 +
294 +                       chmap->map[ch] = g_v2a_position_map[position];
295 +               }
296 +       }
297 +
298 +       /* Create an ALSA control per each PCM device/stream. */
299 +       list_for_each_entry(vpcm, &snd->pcm_list, list) {
300 +               if (!vpcm->pcm)
301 +                       continue;
302 +
303 +               for (i = 0; i < ARRAY_SIZE(vpcm->streams); ++i) {
304 +                       vs = &vpcm->streams[i];
305 +
306 +                       if (!vs->nchmaps)
307 +                               continue;
308 +
309 +                       rc = virtsnd_chmap_add_ctls(vpcm->pcm, i, vs);
310 +                       if (rc)
311 +                               return rc;
312 +               }
313 +       }
314 +
315 +       return 0;
316 +}
317 diff --git a/sound/virtio/virtio_pcm.h b/sound/virtio/virtio_pcm.h
318 index efd0228746cf..1353fdc9bd69 100644
319 --- a/sound/virtio/virtio_pcm.h
320 +++ b/sound/virtio/virtio_pcm.h
321 @@ -63,10 +63,14 @@ struct virtio_pcm_substream {
322   * struct virtio_pcm_stream - VirtIO PCM stream.
323   * @substreams: VirtIO substreams belonging to the stream.
324   * @nsubstreams: Number of substreams.
325 + * @chmaps: Kernel channel maps belonging to the stream.
326 + * @nchmaps: Number of channel maps.
327   */
328  struct virtio_pcm_stream {
329         struct virtio_pcm_substream **substreams;
330         u32 nsubstreams;
331 +       struct snd_pcm_chmap_elem *chmaps;
332 +       u32 nchmaps;
333  };
334  
335  /**