Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ALSA: virtio: introduce jack support

Enumerate all available jacks and create ALSA controls.

At the moment jacks have a simple implementation and can only be used
to receive notifications about a plugged in/out device.

Signed-off-by: Anton Yakovlev <anton.yakovlev@opensynergy.com>
Link: https://lore.kernel.org/r/20210302164709.3142702-8-anton.yakovlev@opensynergy.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>

authored by

Anton Yakovlev and committed by
Takashi Iwai
ca61a41f da76e9f3

+260
+1
sound/virtio/Makefile
··· 5 5 virtio_snd-objs := \ 6 6 virtio_card.o \ 7 7 virtio_ctl_msg.o \ 8 + virtio_jack.o \ 8 9 virtio_pcm.o \ 9 10 virtio_pcm_msg.o \ 10 11 virtio_pcm_ops.o
+14
sound/virtio/virtio_card.c
··· 56 56 struct virtio_snd_event *event) 57 57 { 58 58 switch (le32_to_cpu(event->hdr.code)) { 59 + case VIRTIO_SND_EVT_JACK_CONNECTED: 60 + case VIRTIO_SND_EVT_JACK_DISCONNECTED: 61 + virtsnd_jack_event(snd, event); 62 + break; 59 63 case VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED: 60 64 case VIRTIO_SND_EVT_PCM_XRUN: 61 65 virtsnd_pcm_event(snd, event); ··· 223 219 VIRTIO_SND_CARD_NAME " at %s/%s", 224 220 dev_name(dev->parent), dev_name(dev)); 225 221 222 + rc = virtsnd_jack_parse_cfg(snd); 223 + if (rc) 224 + return rc; 225 + 226 226 rc = virtsnd_pcm_parse_cfg(snd); 227 227 if (rc) 228 228 return rc; 229 + 230 + if (snd->njacks) { 231 + rc = virtsnd_jack_build_devs(snd); 232 + if (rc) 233 + return rc; 234 + } 229 235 230 236 if (snd->nsubstreams) { 231 237 rc = virtsnd_pcm_build_devs(snd);
+12
sound/virtio/virtio_card.h
··· 18 18 #define VIRTIO_SND_CARD_NAME "VirtIO SoundCard" 19 19 #define VIRTIO_SND_PCM_NAME "VirtIO PCM" 20 20 21 + struct virtio_jack; 21 22 struct virtio_pcm_substream; 22 23 23 24 /** ··· 39 38 * @ctl_msgs: Pending control request list. 40 39 * @event_msgs: Device events. 41 40 * @pcm_list: VirtIO PCM device list. 41 + * @jacks: VirtIO jacks. 42 + * @njacks: Number of jacks. 42 43 * @substreams: VirtIO PCM substreams. 43 44 * @nsubstreams: Number of PCM substreams. 44 45 */ ··· 51 48 struct list_head ctl_msgs; 52 49 struct virtio_snd_event *event_msgs; 53 50 struct list_head pcm_list; 51 + struct virtio_jack *jacks; 52 + u32 njacks; 54 53 struct virtio_pcm_substream *substreams; 55 54 u32 nsubstreams; 56 55 }; ··· 92 87 else 93 88 return virtsnd_rx_queue(vss->snd); 94 89 } 90 + 91 + int virtsnd_jack_parse_cfg(struct virtio_snd *snd); 92 + 93 + int virtsnd_jack_build_devs(struct virtio_snd *snd); 94 + 95 + void virtsnd_jack_event(struct virtio_snd *snd, 96 + struct virtio_snd_event *event); 95 97 96 98 #endif /* VIRTIO_SND_CARD_H */
+233
sound/virtio/virtio_jack.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * virtio-snd: Virtio sound device 4 + * Copyright (C) 2021 OpenSynergy GmbH 5 + */ 6 + #include <linux/virtio_config.h> 7 + #include <sound/jack.h> 8 + #include <sound/hda_verbs.h> 9 + 10 + #include "virtio_card.h" 11 + 12 + /** 13 + * DOC: Implementation Status 14 + * 15 + * At the moment jacks have a simple implementation and can only be used to 16 + * receive notifications about a plugged in/out device. 17 + * 18 + * VIRTIO_SND_R_JACK_REMAP 19 + * is not supported 20 + */ 21 + 22 + /** 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). 31 + */ 32 + struct virtio_jack { 33 + struct snd_jack *jack; 34 + u32 nid; 35 + u32 features; 36 + u32 defconf; 37 + u32 caps; 38 + bool connected; 39 + int type; 40 + }; 41 + 42 + /** 43 + * virtsnd_jack_get_label() - Get the name string for the jack. 44 + * @vjack: VirtIO jack. 45 + * 46 + * Returns the jack name based on the default pin configuration value (see HDA 47 + * specification). 48 + * 49 + * Context: Any context. 50 + * Return: Name string. 51 + */ 52 + static const char *virtsnd_jack_get_label(struct virtio_jack *vjack) 53 + { 54 + unsigned int defconf = vjack->defconf; 55 + unsigned int device = 56 + (defconf & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT; 57 + unsigned int location = 58 + (defconf & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT; 59 + 60 + switch (device) { 61 + case AC_JACK_LINE_OUT: 62 + return "Line Out"; 63 + case AC_JACK_SPEAKER: 64 + return "Speaker"; 65 + case AC_JACK_HP_OUT: 66 + return "Headphone"; 67 + case AC_JACK_CD: 68 + return "CD"; 69 + case AC_JACK_SPDIF_OUT: 70 + case AC_JACK_DIG_OTHER_OUT: 71 + if (location == AC_JACK_LOC_HDMI) 72 + return "HDMI Out"; 73 + else 74 + return "SPDIF Out"; 75 + case AC_JACK_LINE_IN: 76 + return "Line"; 77 + case AC_JACK_AUX: 78 + return "Aux"; 79 + case AC_JACK_MIC_IN: 80 + return "Mic"; 81 + case AC_JACK_SPDIF_IN: 82 + return "SPDIF In"; 83 + case AC_JACK_DIG_OTHER_IN: 84 + return "Digital In"; 85 + default: 86 + return "Misc"; 87 + } 88 + } 89 + 90 + /** 91 + * virtsnd_jack_get_type() - Get the type for the jack. 92 + * @vjack: VirtIO jack. 93 + * 94 + * Returns the jack type based on the default pin configuration value (see HDA 95 + * specification). 96 + * 97 + * Context: Any context. 98 + * Return: SND_JACK_XXX value. 99 + */ 100 + static int virtsnd_jack_get_type(struct virtio_jack *vjack) 101 + { 102 + unsigned int defconf = vjack->defconf; 103 + unsigned int device = 104 + (defconf & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT; 105 + 106 + switch (device) { 107 + case AC_JACK_LINE_OUT: 108 + case AC_JACK_SPEAKER: 109 + return SND_JACK_LINEOUT; 110 + case AC_JACK_HP_OUT: 111 + return SND_JACK_HEADPHONE; 112 + case AC_JACK_SPDIF_OUT: 113 + case AC_JACK_DIG_OTHER_OUT: 114 + return SND_JACK_AVOUT; 115 + case AC_JACK_MIC_IN: 116 + return SND_JACK_MICROPHONE; 117 + default: 118 + return SND_JACK_LINEIN; 119 + } 120 + } 121 + 122 + /** 123 + * virtsnd_jack_parse_cfg() - Parse the jack configuration. 124 + * @snd: VirtIO sound device. 125 + * 126 + * This function is called during initial device initialization. 127 + * 128 + * Context: Any context that permits to sleep. 129 + * Return: 0 on success, -errno on failure. 130 + */ 131 + int virtsnd_jack_parse_cfg(struct virtio_snd *snd) 132 + { 133 + struct virtio_device *vdev = snd->vdev; 134 + struct virtio_snd_jack_info *info; 135 + u32 i; 136 + int rc; 137 + 138 + virtio_cread_le(vdev, struct virtio_snd_config, jacks, &snd->njacks); 139 + if (!snd->njacks) 140 + return 0; 141 + 142 + snd->jacks = devm_kcalloc(&vdev->dev, snd->njacks, sizeof(*snd->jacks), 143 + GFP_KERNEL); 144 + if (!snd->jacks) 145 + return -ENOMEM; 146 + 147 + info = kcalloc(snd->njacks, sizeof(*info), GFP_KERNEL); 148 + if (!info) 149 + return -ENOMEM; 150 + 151 + rc = virtsnd_ctl_query_info(snd, VIRTIO_SND_R_JACK_INFO, 0, snd->njacks, 152 + sizeof(*info), info); 153 + if (rc) 154 + goto on_exit; 155 + 156 + for (i = 0; i < snd->njacks; ++i) { 157 + struct virtio_jack *vjack = &snd->jacks[i]; 158 + 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; 164 + } 165 + 166 + on_exit: 167 + kfree(info); 168 + 169 + return rc; 170 + } 171 + 172 + /** 173 + * virtsnd_jack_build_devs() - Build ALSA controls for jacks. 174 + * @snd: VirtIO sound device. 175 + * 176 + * Context: Any context that permits to sleep. 177 + * Return: 0 on success, -errno on failure. 178 + */ 179 + int virtsnd_jack_build_devs(struct virtio_snd *snd) 180 + { 181 + u32 i; 182 + int rc; 183 + 184 + for (i = 0; i < snd->njacks; ++i) { 185 + struct virtio_jack *vjack = &snd->jacks[i]; 186 + 187 + vjack->type = virtsnd_jack_get_type(vjack); 188 + 189 + rc = snd_jack_new(snd->card, virtsnd_jack_get_label(vjack), 190 + vjack->type, &vjack->jack, true, true); 191 + if (rc) 192 + return rc; 193 + 194 + if (vjack->jack) 195 + vjack->jack->private_data = vjack; 196 + 197 + snd_jack_report(vjack->jack, 198 + vjack->connected ? vjack->type : 0); 199 + } 200 + 201 + return 0; 202 + } 203 + 204 + /** 205 + * virtsnd_jack_event() - Handle the jack event notification. 206 + * @snd: VirtIO sound device. 207 + * @event: VirtIO sound event. 208 + * 209 + * Context: Interrupt context. 210 + */ 211 + void virtsnd_jack_event(struct virtio_snd *snd, struct virtio_snd_event *event) 212 + { 213 + u32 jack_id = le32_to_cpu(event->data); 214 + struct virtio_jack *vjack; 215 + 216 + if (jack_id >= snd->njacks) 217 + return; 218 + 219 + vjack = &snd->jacks[jack_id]; 220 + 221 + switch (le32_to_cpu(event->hdr.code)) { 222 + case VIRTIO_SND_EVT_JACK_CONNECTED: 223 + vjack->connected = true; 224 + break; 225 + case VIRTIO_SND_EVT_JACK_DISCONNECTED: 226 + vjack->connected = false; 227 + break; 228 + default: 229 + return; 230 + } 231 + 232 + snd_jack_report(vjack->jack, vjack->connected ? vjack->type : 0); 233 + }