Linux Audio

Check our new training course

Loading...
v6.2
  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}
v6.8
  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}