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);