1 // SPDX-License-Identifier: GPL-2.0+
3 * virtio-snd: Virtio sound device
4 * Copyright (C) 2021 OpenSynergy GmbH
6 #include <linux/virtio_config.h>
7 #include <sound/jack.h>
8 #include <sound/hda_verbs.h>
10 #include "virtio_card.h"
13 * DOC: Implementation Status
15 * At the moment jacks have a simple implementation and can only be used to
16 * receive notifications about a plugged in/out device.
18 * VIRTIO_SND_R_JACK_REMAP
23 * struct virtio_jack - VirtIO jack.
24 * @jack: Kernel jack control.
25 * @nid: Functional group node identifier.
26 * @features: Jack virtio feature bit map (1 << VIRTIO_SND_JACK_F_XXX).
27 * @defconf: Pin default configuration value.
28 * @caps: Pin capabilities value.
29 * @connected: Current jack connection status.
30 * @type: Kernel jack type (SND_JACK_XXX).
33 struct snd_jack *jack;
43 * virtsnd_jack_get_label() - Get the name string for the jack.
44 * @vjack: VirtIO jack.
46 * Returns the jack name based on the default pin configuration value (see HDA
49 * Context: Any context.
50 * Return: Name string.
52 static const char *virtsnd_jack_get_label(struct virtio_jack *vjack)
54 unsigned int defconf = vjack->defconf;
56 (defconf & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT;
57 unsigned int location =
58 (defconf & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
61 case AC_JACK_LINE_OUT:
69 case AC_JACK_SPDIF_OUT:
70 case AC_JACK_DIG_OTHER_OUT:
71 if (location == AC_JACK_LOC_HDMI)
81 case AC_JACK_SPDIF_IN:
83 case AC_JACK_DIG_OTHER_IN:
91 * virtsnd_jack_get_type() - Get the type for the jack.
92 * @vjack: VirtIO jack.
94 * Returns the jack type based on the default pin configuration value (see HDA
97 * Context: Any context.
98 * Return: SND_JACK_XXX value.
100 static int virtsnd_jack_get_type(struct virtio_jack *vjack)
102 unsigned int defconf = vjack->defconf;
103 unsigned int device =
104 (defconf & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT;
107 case AC_JACK_LINE_OUT:
108 case AC_JACK_SPEAKER:
109 return SND_JACK_LINEOUT;
111 return SND_JACK_HEADPHONE;
112 case AC_JACK_SPDIF_OUT:
113 case AC_JACK_DIG_OTHER_OUT:
114 return SND_JACK_AVOUT;
116 return SND_JACK_MICROPHONE;
118 return SND_JACK_LINEIN;
123 * virtsnd_jack_parse_cfg() - Parse the jack configuration.
124 * @snd: VirtIO sound device.
126 * This function is called during initial device initialization.
128 * Context: Any context that permits to sleep.
129 * Return: 0 on success, -errno on failure.
131 int virtsnd_jack_parse_cfg(struct virtio_snd *snd)
133 struct virtio_device *vdev = snd->vdev;
134 struct virtio_snd_jack_info *info;
138 virtio_cread_le(vdev, struct virtio_snd_config, jacks, &snd->njacks);
142 snd->jacks = devm_kcalloc(&vdev->dev, snd->njacks, sizeof(*snd->jacks),
147 info = kcalloc(snd->njacks, sizeof(*info), GFP_KERNEL);
151 rc = virtsnd_ctl_query_info(snd, VIRTIO_SND_R_JACK_INFO, 0, snd->njacks,
152 sizeof(*info), info);
156 for (i = 0; i < snd->njacks; ++i) {
157 struct virtio_jack *vjack = &snd->jacks[i];
159 vjack->nid = le32_to_cpu(info[i].hdr.hda_fn_nid);
160 vjack->features = le32_to_cpu(info[i].features);
161 vjack->defconf = le32_to_cpu(info[i].hda_reg_defconf);
162 vjack->caps = le32_to_cpu(info[i].hda_reg_caps);
163 vjack->connected = info[i].connected;
173 * virtsnd_jack_build_devs() - Build ALSA controls for jacks.
174 * @snd: VirtIO sound device.
176 * Context: Any context that permits to sleep.
177 * Return: 0 on success, -errno on failure.
179 int virtsnd_jack_build_devs(struct virtio_snd *snd)
184 for (i = 0; i < snd->njacks; ++i) {
185 struct virtio_jack *vjack = &snd->jacks[i];
187 vjack->type = virtsnd_jack_get_type(vjack);
189 rc = snd_jack_new(snd->card, virtsnd_jack_get_label(vjack),
190 vjack->type, &vjack->jack, true, true);
195 vjack->jack->private_data = vjack;
197 snd_jack_report(vjack->jack,
198 vjack->connected ? vjack->type : 0);
205 * virtsnd_jack_event() - Handle the jack event notification.
206 * @snd: VirtIO sound device.
207 * @event: VirtIO sound event.
209 * Context: Interrupt context.
211 void virtsnd_jack_event(struct virtio_snd *snd, struct virtio_snd_event *event)
213 u32 jack_id = le32_to_cpu(event->data);
214 struct virtio_jack *vjack;
216 if (jack_id >= snd->njacks)
219 vjack = &snd->jacks[jack_id];
221 switch (le32_to_cpu(event->hdr.code)) {
222 case VIRTIO_SND_EVT_JACK_CONNECTED:
223 vjack->connected = true;
225 case VIRTIO_SND_EVT_JACK_DISCONNECTED:
226 vjack->connected = false;
232 snd_jack_report(vjack->jack, vjack->connected ? vjack->type : 0);