brintos

brintos / linux-shallow public Read only

0
0
Text · 5.3 KiB · c69f1dc Raw
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