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
6 Enumerate all available jacks and create ALSA controls.
8 At the moment jacks have a simple implementation and can only be used
9 to receive notifications about a plugged in/out device.
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>
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
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
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)
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);
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));
54 + rc = virtsnd_jack_parse_cfg(snd);
58 rc = virtsnd_pcm_parse_cfg(snd);
63 + rc = virtsnd_jack_build_devs(snd);
68 if (snd->nsubstreams) {
69 rc = virtsnd_pcm_build_devs(snd);
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
76 #define VIRTIO_SND_CARD_NAME "VirtIO SoundCard"
77 #define VIRTIO_SND_PCM_NAME "VirtIO PCM"
80 struct virtio_pcm_substream;
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.
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;
98 struct virtio_pcm_substream *substreams;
101 @@ -88,4 +93,11 @@ virtsnd_pcm_queue(struct virtio_pcm_substream *vss)
102 return virtsnd_rx_queue(vss->snd);
105 +int virtsnd_jack_parse_cfg(struct virtio_snd *snd);
107 +int virtsnd_jack_build_devs(struct virtio_snd *snd);
109 +void virtsnd_jack_event(struct virtio_snd *snd,
110 + struct virtio_snd_event *event);
112 #endif /* VIRTIO_SND_CARD_H */
113 diff --git a/sound/virtio/virtio_jack.c b/sound/virtio/virtio_jack.c
115 index 000000000000..c69f1dcdcc84
117 +++ b/sound/virtio/virtio_jack.c
119 +// SPDX-License-Identifier: GPL-2.0+
121 + * virtio-snd: Virtio sound device
122 + * Copyright (C) 2021 OpenSynergy GmbH
124 +#include <linux/virtio_config.h>
125 +#include <sound/jack.h>
126 +#include <sound/hda_verbs.h>
128 +#include "virtio_card.h"
131 + * DOC: Implementation Status
133 + * At the moment jacks have a simple implementation and can only be used to
134 + * receive notifications about a plugged in/out device.
136 + * VIRTIO_SND_R_JACK_REMAP
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).
150 +struct virtio_jack {
151 + struct snd_jack *jack;
161 + * virtsnd_jack_get_label() - Get the name string for the jack.
162 + * @vjack: VirtIO jack.
164 + * Returns the jack name based on the default pin configuration value (see HDA
167 + * Context: Any context.
168 + * Return: Name string.
170 +static const char *virtsnd_jack_get_label(struct virtio_jack *vjack)
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;
179 + case AC_JACK_LINE_OUT:
181 + case AC_JACK_SPEAKER:
183 + case AC_JACK_HP_OUT:
184 + return "Headphone";
187 + case AC_JACK_SPDIF_OUT:
188 + case AC_JACK_DIG_OTHER_OUT:
189 + if (location == AC_JACK_LOC_HDMI)
192 + return "SPDIF Out";
193 + case AC_JACK_LINE_IN:
197 + case AC_JACK_MIC_IN:
199 + case AC_JACK_SPDIF_IN:
201 + case AC_JACK_DIG_OTHER_IN:
202 + return "Digital In";
209 + * virtsnd_jack_get_type() - Get the type for the jack.
210 + * @vjack: VirtIO jack.
212 + * Returns the jack type based on the default pin configuration value (see HDA
215 + * Context: Any context.
216 + * Return: SND_JACK_XXX value.
218 +static int virtsnd_jack_get_type(struct virtio_jack *vjack)
220 + unsigned int defconf = vjack->defconf;
221 + unsigned int device =
222 + (defconf & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT;
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;
236 + return SND_JACK_LINEIN;
241 + * virtsnd_jack_parse_cfg() - Parse the jack configuration.
242 + * @snd: VirtIO sound device.
244 + * This function is called during initial device initialization.
246 + * Context: Any context that permits to sleep.
247 + * Return: 0 on success, -errno on failure.
249 +int virtsnd_jack_parse_cfg(struct virtio_snd *snd)
251 + struct virtio_device *vdev = snd->vdev;
252 + struct virtio_snd_jack_info *info;
256 + virtio_cread_le(vdev, struct virtio_snd_config, jacks, &snd->njacks);
260 + snd->jacks = devm_kcalloc(&vdev->dev, snd->njacks, sizeof(*snd->jacks),
265 + info = kcalloc(snd->njacks, sizeof(*info), GFP_KERNEL);
269 + rc = virtsnd_ctl_query_info(snd, VIRTIO_SND_R_JACK_INFO, 0, snd->njacks,
270 + sizeof(*info), info);
274 + for (i = 0; i < snd->njacks; ++i) {
275 + struct virtio_jack *vjack = &snd->jacks[i];
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;
291 + * virtsnd_jack_build_devs() - Build ALSA controls for jacks.
292 + * @snd: VirtIO sound device.
294 + * Context: Any context that permits to sleep.
295 + * Return: 0 on success, -errno on failure.
297 +int virtsnd_jack_build_devs(struct virtio_snd *snd)
302 + for (i = 0; i < snd->njacks; ++i) {
303 + struct virtio_jack *vjack = &snd->jacks[i];
305 + vjack->type = virtsnd_jack_get_type(vjack);
307 + rc = snd_jack_new(snd->card, virtsnd_jack_get_label(vjack),
308 + vjack->type, &vjack->jack, true, true);
313 + vjack->jack->private_data = vjack;
315 + snd_jack_report(vjack->jack,
316 + vjack->connected ? vjack->type : 0);
323 + * virtsnd_jack_event() - Handle the jack event notification.
324 + * @snd: VirtIO sound device.
325 + * @event: VirtIO sound event.
327 + * Context: Interrupt context.
329 +void virtsnd_jack_event(struct virtio_snd *snd, struct virtio_snd_event *event)
331 + u32 jack_id = le32_to_cpu(event->data);
332 + struct virtio_jack *vjack;
334 + if (jack_id >= snd->njacks)
337 + vjack = &snd->jacks[jack_id];
339 + switch (le32_to_cpu(event->hdr.code)) {
340 + case VIRTIO_SND_EVT_JACK_CONNECTED:
341 + vjack->connected = true;
343 + case VIRTIO_SND_EVT_JACK_DISCONNECTED:
344 + vjack->connected = false;
350 + snd_jack_report(vjack->jack, vjack->connected ? vjack->type : 0);