cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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}