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