linux/sound/virtio/virtio_jack.c
<<
>>
Prefs
   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}
 234