Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * virtio-snd: Virtio sound device
0004  * Copyright (C) 2021 OpenSynergy GmbH
0005  */
0006 #include <linux/virtio_config.h>
0007 #include <sound/jack.h>
0008 #include <sound/hda_verbs.h>
0009 
0010 #include "virtio_card.h"
0011 
0012 /**
0013  * DOC: Implementation Status
0014  *
0015  * At the moment jacks have a simple implementation and can only be used to
0016  * receive notifications about a plugged in/out device.
0017  *
0018  * VIRTIO_SND_R_JACK_REMAP
0019  *   is not supported
0020  */
0021 
0022 /**
0023  * struct virtio_jack - VirtIO jack.
0024  * @jack: Kernel jack control.
0025  * @nid: Functional group node identifier.
0026  * @features: Jack virtio feature bit map (1 << VIRTIO_SND_JACK_F_XXX).
0027  * @defconf: Pin default configuration value.
0028  * @caps: Pin capabilities value.
0029  * @connected: Current jack connection status.
0030  * @type: Kernel jack type (SND_JACK_XXX).
0031  */
0032 struct virtio_jack {
0033     struct snd_jack *jack;
0034     u32 nid;
0035     u32 features;
0036     u32 defconf;
0037     u32 caps;
0038     bool connected;
0039     int type;
0040 };
0041 
0042 /**
0043  * virtsnd_jack_get_label() - Get the name string for the jack.
0044  * @vjack: VirtIO jack.
0045  *
0046  * Returns the jack name based on the default pin configuration value (see HDA
0047  * specification).
0048  *
0049  * Context: Any context.
0050  * Return: Name string.
0051  */
0052 static const char *virtsnd_jack_get_label(struct virtio_jack *vjack)
0053 {
0054     unsigned int defconf = vjack->defconf;
0055     unsigned int device =
0056         (defconf & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT;
0057     unsigned int location =
0058         (defconf & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
0059 
0060     switch (device) {
0061     case AC_JACK_LINE_OUT:
0062         return "Line Out";
0063     case AC_JACK_SPEAKER:
0064         return "Speaker";
0065     case AC_JACK_HP_OUT:
0066         return "Headphone";
0067     case AC_JACK_CD:
0068         return "CD";
0069     case AC_JACK_SPDIF_OUT:
0070     case AC_JACK_DIG_OTHER_OUT:
0071         if (location == AC_JACK_LOC_HDMI)
0072             return "HDMI Out";
0073         else
0074             return "SPDIF Out";
0075     case AC_JACK_LINE_IN:
0076         return "Line";
0077     case AC_JACK_AUX:
0078         return "Aux";
0079     case AC_JACK_MIC_IN:
0080         return "Mic";
0081     case AC_JACK_SPDIF_IN:
0082         return "SPDIF In";
0083     case AC_JACK_DIG_OTHER_IN:
0084         return "Digital In";
0085     default:
0086         return "Misc";
0087     }
0088 }
0089 
0090 /**
0091  * virtsnd_jack_get_type() - Get the type for the jack.
0092  * @vjack: VirtIO jack.
0093  *
0094  * Returns the jack type based on the default pin configuration value (see HDA
0095  * specification).
0096  *
0097  * Context: Any context.
0098  * Return: SND_JACK_XXX value.
0099  */
0100 static int virtsnd_jack_get_type(struct virtio_jack *vjack)
0101 {
0102     unsigned int defconf = vjack->defconf;
0103     unsigned int device =
0104         (defconf & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT;
0105 
0106     switch (device) {
0107     case AC_JACK_LINE_OUT:
0108     case AC_JACK_SPEAKER:
0109         return SND_JACK_LINEOUT;
0110     case AC_JACK_HP_OUT:
0111         return SND_JACK_HEADPHONE;
0112     case AC_JACK_SPDIF_OUT:
0113     case AC_JACK_DIG_OTHER_OUT:
0114         return SND_JACK_AVOUT;
0115     case AC_JACK_MIC_IN:
0116         return SND_JACK_MICROPHONE;
0117     default:
0118         return SND_JACK_LINEIN;
0119     }
0120 }
0121 
0122 /**
0123  * virtsnd_jack_parse_cfg() - Parse the jack configuration.
0124  * @snd: VirtIO sound device.
0125  *
0126  * This function is called during initial device initialization.
0127  *
0128  * Context: Any context that permits to sleep.
0129  * Return: 0 on success, -errno on failure.
0130  */
0131 int virtsnd_jack_parse_cfg(struct virtio_snd *snd)
0132 {
0133     struct virtio_device *vdev = snd->vdev;
0134     struct virtio_snd_jack_info *info;
0135     u32 i;
0136     int rc;
0137 
0138     virtio_cread_le(vdev, struct virtio_snd_config, jacks, &snd->njacks);
0139     if (!snd->njacks)
0140         return 0;
0141 
0142     snd->jacks = devm_kcalloc(&vdev->dev, snd->njacks, sizeof(*snd->jacks),
0143                   GFP_KERNEL);
0144     if (!snd->jacks)
0145         return -ENOMEM;
0146 
0147     info = kcalloc(snd->njacks, sizeof(*info), GFP_KERNEL);
0148     if (!info)
0149         return -ENOMEM;
0150 
0151     rc = virtsnd_ctl_query_info(snd, VIRTIO_SND_R_JACK_INFO, 0, snd->njacks,
0152                     sizeof(*info), info);
0153     if (rc)
0154         goto on_exit;
0155 
0156     for (i = 0; i < snd->njacks; ++i) {
0157         struct virtio_jack *vjack = &snd->jacks[i];
0158 
0159         vjack->nid = le32_to_cpu(info[i].hdr.hda_fn_nid);
0160         vjack->features = le32_to_cpu(info[i].features);
0161         vjack->defconf = le32_to_cpu(info[i].hda_reg_defconf);
0162         vjack->caps = le32_to_cpu(info[i].hda_reg_caps);
0163         vjack->connected = info[i].connected;
0164     }
0165 
0166 on_exit:
0167     kfree(info);
0168 
0169     return rc;
0170 }
0171 
0172 /**
0173  * virtsnd_jack_build_devs() - Build ALSA controls for jacks.
0174  * @snd: VirtIO sound device.
0175  *
0176  * Context: Any context that permits to sleep.
0177  * Return: 0 on success, -errno on failure.
0178  */
0179 int virtsnd_jack_build_devs(struct virtio_snd *snd)
0180 {
0181     u32 i;
0182     int rc;
0183 
0184     for (i = 0; i < snd->njacks; ++i) {
0185         struct virtio_jack *vjack = &snd->jacks[i];
0186 
0187         vjack->type = virtsnd_jack_get_type(vjack);
0188 
0189         rc = snd_jack_new(snd->card, virtsnd_jack_get_label(vjack),
0190                   vjack->type, &vjack->jack, true, true);
0191         if (rc)
0192             return rc;
0193 
0194         if (vjack->jack)
0195             vjack->jack->private_data = vjack;
0196 
0197         snd_jack_report(vjack->jack,
0198                 vjack->connected ? vjack->type : 0);
0199     }
0200 
0201     return 0;
0202 }
0203 
0204 /**
0205  * virtsnd_jack_event() - Handle the jack event notification.
0206  * @snd: VirtIO sound device.
0207  * @event: VirtIO sound event.
0208  *
0209  * Context: Interrupt context.
0210  */
0211 void virtsnd_jack_event(struct virtio_snd *snd, struct virtio_snd_event *event)
0212 {
0213     u32 jack_id = le32_to_cpu(event->data);
0214     struct virtio_jack *vjack;
0215 
0216     if (jack_id >= snd->njacks)
0217         return;
0218 
0219     vjack = &snd->jacks[jack_id];
0220 
0221     switch (le32_to_cpu(event->hdr.code)) {
0222     case VIRTIO_SND_EVT_JACK_CONNECTED:
0223         vjack->connected = true;
0224         break;
0225     case VIRTIO_SND_EVT_JACK_DISCONNECTED:
0226         vjack->connected = false;
0227         break;
0228     default:
0229         return;
0230     }
0231 
0232     snd_jack_report(vjack->jack, vjack->connected ? vjack->type : 0);
0233 }