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 / 0007-ALSA-virtio-introduce-jack-support.patch
1 From 07692f250a96382b38daa2b7e2b96689f64d7a40 Mon Sep 17 00:00:00 2001
2 From: Anton Yakovlev <anton.yakovlev@opensynergy.com>
3 Date: Tue, 2 Mar 2021 17:47:07 +0100
4 Subject: [PATCH] ALSA: virtio: introduce jack support
5
6 Enumerate all available jacks and create ALSA controls.
7
8 At the moment jacks have a simple implementation and can only be used
9 to receive notifications about a plugged in/out device.
10
11 Signed-off-by: Anton Yakovlev <anton.yakovlev@opensynergy.com>
12 Link: https://lore.kernel.org/r/20210302164709.3142702-8-anton.yakovlev@opensynergy.com
13 Signed-off-by: Takashi Iwai <tiwai@suse.de>
14 Signed-off-by: Vasyl Vavrychuk <vasyl.vavrychuk@opensynergy.com>
15 ---
16  sound/virtio/Makefile      |   1 +
17  sound/virtio/virtio_card.c |  14 +++
18  sound/virtio/virtio_card.h |  12 ++
19  sound/virtio/virtio_jack.c | 233 +++++++++++++++++++++++++++++++++++++
20  4 files changed, 260 insertions(+)
21  create mode 100644 sound/virtio/virtio_jack.c
22
23 diff --git a/sound/virtio/Makefile b/sound/virtio/Makefile
24 index 34493226793f..09f485291285 100644
25 --- a/sound/virtio/Makefile
26 +++ b/sound/virtio/Makefile
27 @@ -5,6 +5,7 @@ obj-$(CONFIG_SND_VIRTIO) += virtio_snd.o
28  virtio_snd-objs := \
29         virtio_card.o \
30         virtio_ctl_msg.o \
31 +       virtio_jack.o \
32         virtio_pcm.o \
33         virtio_pcm_msg.o \
34         virtio_pcm_ops.o
35 diff --git a/sound/virtio/virtio_card.c b/sound/virtio/virtio_card.c
36 index 57b9b7f3a9c0..89bd66c1256e 100644
37 --- a/sound/virtio/virtio_card.c
38 +++ b/sound/virtio/virtio_card.c
39 @@ -56,6 +56,10 @@ static void virtsnd_event_dispatch(struct virtio_snd *snd,
40                                    struct virtio_snd_event *event)
41  {
42         switch (le32_to_cpu(event->hdr.code)) {
43 +       case VIRTIO_SND_EVT_JACK_CONNECTED:
44 +       case VIRTIO_SND_EVT_JACK_DISCONNECTED:
45 +               virtsnd_jack_event(snd, event);
46 +               break;
47         case VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED:
48         case VIRTIO_SND_EVT_PCM_XRUN:
49                 virtsnd_pcm_event(snd, event);
50 @@ -219,10 +223,20 @@ static int virtsnd_build_devs(struct virtio_snd *snd)
51                          VIRTIO_SND_CARD_NAME " at %s/%s",
52                          dev_name(dev->parent), dev_name(dev));
53  
54 +       rc = virtsnd_jack_parse_cfg(snd);
55 +       if (rc)
56 +               return rc;
57 +
58         rc = virtsnd_pcm_parse_cfg(snd);
59         if (rc)
60                 return rc;
61  
62 +       if (snd->njacks) {
63 +               rc = virtsnd_jack_build_devs(snd);
64 +               if (rc)
65 +                       return rc;
66 +       }
67 +
68         if (snd->nsubstreams) {
69                 rc = virtsnd_pcm_build_devs(snd);
70                 if (rc)
71 diff --git a/sound/virtio/virtio_card.h b/sound/virtio/virtio_card.h
72 index c43f9744d362..f154313c79fd 100644
73 --- a/sound/virtio/virtio_card.h
74 +++ b/sound/virtio/virtio_card.h
75 @@ -18,6 +18,7 @@
76  #define VIRTIO_SND_CARD_NAME   "VirtIO SoundCard"
77  #define VIRTIO_SND_PCM_NAME    "VirtIO PCM"
78  
79 +struct virtio_jack;
80  struct virtio_pcm_substream;
81  
82  /**
83 @@ -38,6 +39,8 @@ struct virtio_snd_queue {
84   * @ctl_msgs: Pending control request list.
85   * @event_msgs: Device events.
86   * @pcm_list: VirtIO PCM device list.
87 + * @jacks: VirtIO jacks.
88 + * @njacks: Number of jacks.
89   * @substreams: VirtIO PCM substreams.
90   * @nsubstreams: Number of PCM substreams.
91   */
92 @@ -48,6 +51,8 @@ struct virtio_snd {
93         struct list_head ctl_msgs;
94         struct virtio_snd_event *event_msgs;
95         struct list_head pcm_list;
96 +       struct virtio_jack *jacks;
97 +       u32 njacks;
98         struct virtio_pcm_substream *substreams;
99         u32 nsubstreams;
100  };
101 @@ -88,4 +93,11 @@ virtsnd_pcm_queue(struct virtio_pcm_substream *vss)
102                 return virtsnd_rx_queue(vss->snd);
103  }
104  
105 +int virtsnd_jack_parse_cfg(struct virtio_snd *snd);
106 +
107 +int virtsnd_jack_build_devs(struct virtio_snd *snd);
108 +
109 +void virtsnd_jack_event(struct virtio_snd *snd,
110 +                       struct virtio_snd_event *event);
111 +
112  #endif /* VIRTIO_SND_CARD_H */
113 diff --git a/sound/virtio/virtio_jack.c b/sound/virtio/virtio_jack.c
114 new file mode 100644
115 index 000000000000..c69f1dcdcc84
116 --- /dev/null
117 +++ b/sound/virtio/virtio_jack.c
118 @@ -0,0 +1,233 @@
119 +// SPDX-License-Identifier: GPL-2.0+
120 +/*
121 + * virtio-snd: Virtio sound device
122 + * Copyright (C) 2021 OpenSynergy GmbH
123 + */
124 +#include <linux/virtio_config.h>
125 +#include <sound/jack.h>
126 +#include <sound/hda_verbs.h>
127 +
128 +#include "virtio_card.h"
129 +
130 +/**
131 + * DOC: Implementation Status
132 + *
133 + * At the moment jacks have a simple implementation and can only be used to
134 + * receive notifications about a plugged in/out device.
135 + *
136 + * VIRTIO_SND_R_JACK_REMAP
137 + *   is not supported
138 + */
139 +
140 +/**
141 + * struct virtio_jack - VirtIO jack.
142 + * @jack: Kernel jack control.
143 + * @nid: Functional group node identifier.
144 + * @features: Jack virtio feature bit map (1 << VIRTIO_SND_JACK_F_XXX).
145 + * @defconf: Pin default configuration value.
146 + * @caps: Pin capabilities value.
147 + * @connected: Current jack connection status.
148 + * @type: Kernel jack type (SND_JACK_XXX).
149 + */
150 +struct virtio_jack {
151 +       struct snd_jack *jack;
152 +       u32 nid;
153 +       u32 features;
154 +       u32 defconf;
155 +       u32 caps;
156 +       bool connected;
157 +       int type;
158 +};
159 +
160 +/**
161 + * virtsnd_jack_get_label() - Get the name string for the jack.
162 + * @vjack: VirtIO jack.
163 + *
164 + * Returns the jack name based on the default pin configuration value (see HDA
165 + * specification).
166 + *
167 + * Context: Any context.
168 + * Return: Name string.
169 + */
170 +static const char *virtsnd_jack_get_label(struct virtio_jack *vjack)
171 +{
172 +       unsigned int defconf = vjack->defconf;
173 +       unsigned int device =
174 +               (defconf & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT;
175 +       unsigned int location =
176 +               (defconf & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
177 +
178 +       switch (device) {
179 +       case AC_JACK_LINE_OUT:
180 +               return "Line Out";
181 +       case AC_JACK_SPEAKER:
182 +               return "Speaker";
183 +       case AC_JACK_HP_OUT:
184 +               return "Headphone";
185 +       case AC_JACK_CD:
186 +               return "CD";
187 +       case AC_JACK_SPDIF_OUT:
188 +       case AC_JACK_DIG_OTHER_OUT:
189 +               if (location == AC_JACK_LOC_HDMI)
190 +                       return "HDMI Out";
191 +               else
192 +                       return "SPDIF Out";
193 +       case AC_JACK_LINE_IN:
194 +               return "Line";
195 +       case AC_JACK_AUX:
196 +               return "Aux";
197 +       case AC_JACK_MIC_IN:
198 +               return "Mic";
199 +       case AC_JACK_SPDIF_IN:
200 +               return "SPDIF In";
201 +       case AC_JACK_DIG_OTHER_IN:
202 +               return "Digital In";
203 +       default:
204 +               return "Misc";
205 +       }
206 +}
207 +
208 +/**
209 + * virtsnd_jack_get_type() - Get the type for the jack.
210 + * @vjack: VirtIO jack.
211 + *
212 + * Returns the jack type based on the default pin configuration value (see HDA
213 + * specification).
214 + *
215 + * Context: Any context.
216 + * Return: SND_JACK_XXX value.
217 + */
218 +static int virtsnd_jack_get_type(struct virtio_jack *vjack)
219 +{
220 +       unsigned int defconf = vjack->defconf;
221 +       unsigned int device =
222 +               (defconf & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT;
223 +
224 +       switch (device) {
225 +       case AC_JACK_LINE_OUT:
226 +       case AC_JACK_SPEAKER:
227 +               return SND_JACK_LINEOUT;
228 +       case AC_JACK_HP_OUT:
229 +               return SND_JACK_HEADPHONE;
230 +       case AC_JACK_SPDIF_OUT:
231 +       case AC_JACK_DIG_OTHER_OUT:
232 +               return SND_JACK_AVOUT;
233 +       case AC_JACK_MIC_IN:
234 +               return SND_JACK_MICROPHONE;
235 +       default:
236 +               return SND_JACK_LINEIN;
237 +       }
238 +}
239 +
240 +/**
241 + * virtsnd_jack_parse_cfg() - Parse the jack configuration.
242 + * @snd: VirtIO sound device.
243 + *
244 + * This function is called during initial device initialization.
245 + *
246 + * Context: Any context that permits to sleep.
247 + * Return: 0 on success, -errno on failure.
248 + */
249 +int virtsnd_jack_parse_cfg(struct virtio_snd *snd)
250 +{
251 +       struct virtio_device *vdev = snd->vdev;
252 +       struct virtio_snd_jack_info *info;
253 +       u32 i;
254 +       int rc;
255 +
256 +       virtio_cread_le(vdev, struct virtio_snd_config, jacks, &snd->njacks);
257 +       if (!snd->njacks)
258 +               return 0;
259 +
260 +       snd->jacks = devm_kcalloc(&vdev->dev, snd->njacks, sizeof(*snd->jacks),
261 +                                 GFP_KERNEL);
262 +       if (!snd->jacks)
263 +               return -ENOMEM;
264 +
265 +       info = kcalloc(snd->njacks, sizeof(*info), GFP_KERNEL);
266 +       if (!info)
267 +               return -ENOMEM;
268 +
269 +       rc = virtsnd_ctl_query_info(snd, VIRTIO_SND_R_JACK_INFO, 0, snd->njacks,
270 +                                   sizeof(*info), info);
271 +       if (rc)
272 +               goto on_exit;
273 +
274 +       for (i = 0; i < snd->njacks; ++i) {
275 +               struct virtio_jack *vjack = &snd->jacks[i];
276 +
277 +               vjack->nid = le32_to_cpu(info[i].hdr.hda_fn_nid);
278 +               vjack->features = le32_to_cpu(info[i].features);
279 +               vjack->defconf = le32_to_cpu(info[i].hda_reg_defconf);
280 +               vjack->caps = le32_to_cpu(info[i].hda_reg_caps);
281 +               vjack->connected = info[i].connected;
282 +       }
283 +
284 +on_exit:
285 +       kfree(info);
286 +
287 +       return rc;
288 +}
289 +
290 +/**
291 + * virtsnd_jack_build_devs() - Build ALSA controls for jacks.
292 + * @snd: VirtIO sound device.
293 + *
294 + * Context: Any context that permits to sleep.
295 + * Return: 0 on success, -errno on failure.
296 + */
297 +int virtsnd_jack_build_devs(struct virtio_snd *snd)
298 +{
299 +       u32 i;
300 +       int rc;
301 +
302 +       for (i = 0; i < snd->njacks; ++i) {
303 +               struct virtio_jack *vjack = &snd->jacks[i];
304 +
305 +               vjack->type = virtsnd_jack_get_type(vjack);
306 +
307 +               rc = snd_jack_new(snd->card, virtsnd_jack_get_label(vjack),
308 +                                 vjack->type, &vjack->jack, true, true);
309 +               if (rc)
310 +                       return rc;
311 +
312 +               if (vjack->jack)
313 +                       vjack->jack->private_data = vjack;
314 +
315 +               snd_jack_report(vjack->jack,
316 +                               vjack->connected ? vjack->type : 0);
317 +       }
318 +
319 +       return 0;
320 +}
321 +
322 +/**
323 + * virtsnd_jack_event() - Handle the jack event notification.
324 + * @snd: VirtIO sound device.
325 + * @event: VirtIO sound event.
326 + *
327 + * Context: Interrupt context.
328 + */
329 +void virtsnd_jack_event(struct virtio_snd *snd, struct virtio_snd_event *event)
330 +{
331 +       u32 jack_id = le32_to_cpu(event->data);
332 +       struct virtio_jack *vjack;
333 +
334 +       if (jack_id >= snd->njacks)
335 +               return;
336 +
337 +       vjack = &snd->jacks[jack_id];
338 +
339 +       switch (le32_to_cpu(event->hdr.code)) {
340 +       case VIRTIO_SND_EVT_JACK_CONNECTED:
341 +               vjack->connected = true;
342 +               break;
343 +       case VIRTIO_SND_EVT_JACK_DISCONNECTED:
344 +               vjack->connected = false;
345 +               break;
346 +       default:
347 +               return;
348 +       }
349 +
350 +       snd_jack_report(vjack->jack, vjack->connected ? vjack->type : 0);
351 +}