virtio_jack.c (5378B)
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 */ 32struct 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 */ 52static 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 */ 100static 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 */ 131int 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 166on_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 */ 179int 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 */ 211void 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}