234 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * virtio-snd: Virtio sound device4 * Copyright (C) 2021 OpenSynergy GmbH5 */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 Status14 *15 * At the moment jacks have a simple implementation and can only be used to16 * receive notifications about a plugged in/out device.17 *18 * VIRTIO_SND_R_JACK_REMAP19 * is not supported20 */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 HDA47 * 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 else74 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 HDA95 * 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}234