xref: /openbmc/linux/sound/virtio/virtio_jack.c (revision 8be98d2f2a0a262f8bf8a0bc1fdf522b3c7aab17)
1*ca61a41fSAnton Yakovlev // SPDX-License-Identifier: GPL-2.0+
2*ca61a41fSAnton Yakovlev /*
3*ca61a41fSAnton Yakovlev  * virtio-snd: Virtio sound device
4*ca61a41fSAnton Yakovlev  * Copyright (C) 2021 OpenSynergy GmbH
5*ca61a41fSAnton Yakovlev  */
6*ca61a41fSAnton Yakovlev #include <linux/virtio_config.h>
7*ca61a41fSAnton Yakovlev #include <sound/jack.h>
8*ca61a41fSAnton Yakovlev #include <sound/hda_verbs.h>
9*ca61a41fSAnton Yakovlev 
10*ca61a41fSAnton Yakovlev #include "virtio_card.h"
11*ca61a41fSAnton Yakovlev 
12*ca61a41fSAnton Yakovlev /**
13*ca61a41fSAnton Yakovlev  * DOC: Implementation Status
14*ca61a41fSAnton Yakovlev  *
15*ca61a41fSAnton Yakovlev  * At the moment jacks have a simple implementation and can only be used to
16*ca61a41fSAnton Yakovlev  * receive notifications about a plugged in/out device.
17*ca61a41fSAnton Yakovlev  *
18*ca61a41fSAnton Yakovlev  * VIRTIO_SND_R_JACK_REMAP
19*ca61a41fSAnton Yakovlev  *   is not supported
20*ca61a41fSAnton Yakovlev  */
21*ca61a41fSAnton Yakovlev 
22*ca61a41fSAnton Yakovlev /**
23*ca61a41fSAnton Yakovlev  * struct virtio_jack - VirtIO jack.
24*ca61a41fSAnton Yakovlev  * @jack: Kernel jack control.
25*ca61a41fSAnton Yakovlev  * @nid: Functional group node identifier.
26*ca61a41fSAnton Yakovlev  * @features: Jack virtio feature bit map (1 << VIRTIO_SND_JACK_F_XXX).
27*ca61a41fSAnton Yakovlev  * @defconf: Pin default configuration value.
28*ca61a41fSAnton Yakovlev  * @caps: Pin capabilities value.
29*ca61a41fSAnton Yakovlev  * @connected: Current jack connection status.
30*ca61a41fSAnton Yakovlev  * @type: Kernel jack type (SND_JACK_XXX).
31*ca61a41fSAnton Yakovlev  */
32*ca61a41fSAnton Yakovlev struct virtio_jack {
33*ca61a41fSAnton Yakovlev 	struct snd_jack *jack;
34*ca61a41fSAnton Yakovlev 	u32 nid;
35*ca61a41fSAnton Yakovlev 	u32 features;
36*ca61a41fSAnton Yakovlev 	u32 defconf;
37*ca61a41fSAnton Yakovlev 	u32 caps;
38*ca61a41fSAnton Yakovlev 	bool connected;
39*ca61a41fSAnton Yakovlev 	int type;
40*ca61a41fSAnton Yakovlev };
41*ca61a41fSAnton Yakovlev 
42*ca61a41fSAnton Yakovlev /**
43*ca61a41fSAnton Yakovlev  * virtsnd_jack_get_label() - Get the name string for the jack.
44*ca61a41fSAnton Yakovlev  * @vjack: VirtIO jack.
45*ca61a41fSAnton Yakovlev  *
46*ca61a41fSAnton Yakovlev  * Returns the jack name based on the default pin configuration value (see HDA
47*ca61a41fSAnton Yakovlev  * specification).
48*ca61a41fSAnton Yakovlev  *
49*ca61a41fSAnton Yakovlev  * Context: Any context.
50*ca61a41fSAnton Yakovlev  * Return: Name string.
51*ca61a41fSAnton Yakovlev  */
virtsnd_jack_get_label(struct virtio_jack * vjack)52*ca61a41fSAnton Yakovlev static const char *virtsnd_jack_get_label(struct virtio_jack *vjack)
53*ca61a41fSAnton Yakovlev {
54*ca61a41fSAnton Yakovlev 	unsigned int defconf = vjack->defconf;
55*ca61a41fSAnton Yakovlev 	unsigned int device =
56*ca61a41fSAnton Yakovlev 		(defconf & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT;
57*ca61a41fSAnton Yakovlev 	unsigned int location =
58*ca61a41fSAnton Yakovlev 		(defconf & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
59*ca61a41fSAnton Yakovlev 
60*ca61a41fSAnton Yakovlev 	switch (device) {
61*ca61a41fSAnton Yakovlev 	case AC_JACK_LINE_OUT:
62*ca61a41fSAnton Yakovlev 		return "Line Out";
63*ca61a41fSAnton Yakovlev 	case AC_JACK_SPEAKER:
64*ca61a41fSAnton Yakovlev 		return "Speaker";
65*ca61a41fSAnton Yakovlev 	case AC_JACK_HP_OUT:
66*ca61a41fSAnton Yakovlev 		return "Headphone";
67*ca61a41fSAnton Yakovlev 	case AC_JACK_CD:
68*ca61a41fSAnton Yakovlev 		return "CD";
69*ca61a41fSAnton Yakovlev 	case AC_JACK_SPDIF_OUT:
70*ca61a41fSAnton Yakovlev 	case AC_JACK_DIG_OTHER_OUT:
71*ca61a41fSAnton Yakovlev 		if (location == AC_JACK_LOC_HDMI)
72*ca61a41fSAnton Yakovlev 			return "HDMI Out";
73*ca61a41fSAnton Yakovlev 		else
74*ca61a41fSAnton Yakovlev 			return "SPDIF Out";
75*ca61a41fSAnton Yakovlev 	case AC_JACK_LINE_IN:
76*ca61a41fSAnton Yakovlev 		return "Line";
77*ca61a41fSAnton Yakovlev 	case AC_JACK_AUX:
78*ca61a41fSAnton Yakovlev 		return "Aux";
79*ca61a41fSAnton Yakovlev 	case AC_JACK_MIC_IN:
80*ca61a41fSAnton Yakovlev 		return "Mic";
81*ca61a41fSAnton Yakovlev 	case AC_JACK_SPDIF_IN:
82*ca61a41fSAnton Yakovlev 		return "SPDIF In";
83*ca61a41fSAnton Yakovlev 	case AC_JACK_DIG_OTHER_IN:
84*ca61a41fSAnton Yakovlev 		return "Digital In";
85*ca61a41fSAnton Yakovlev 	default:
86*ca61a41fSAnton Yakovlev 		return "Misc";
87*ca61a41fSAnton Yakovlev 	}
88*ca61a41fSAnton Yakovlev }
89*ca61a41fSAnton Yakovlev 
90*ca61a41fSAnton Yakovlev /**
91*ca61a41fSAnton Yakovlev  * virtsnd_jack_get_type() - Get the type for the jack.
92*ca61a41fSAnton Yakovlev  * @vjack: VirtIO jack.
93*ca61a41fSAnton Yakovlev  *
94*ca61a41fSAnton Yakovlev  * Returns the jack type based on the default pin configuration value (see HDA
95*ca61a41fSAnton Yakovlev  * specification).
96*ca61a41fSAnton Yakovlev  *
97*ca61a41fSAnton Yakovlev  * Context: Any context.
98*ca61a41fSAnton Yakovlev  * Return: SND_JACK_XXX value.
99*ca61a41fSAnton Yakovlev  */
virtsnd_jack_get_type(struct virtio_jack * vjack)100*ca61a41fSAnton Yakovlev static int virtsnd_jack_get_type(struct virtio_jack *vjack)
101*ca61a41fSAnton Yakovlev {
102*ca61a41fSAnton Yakovlev 	unsigned int defconf = vjack->defconf;
103*ca61a41fSAnton Yakovlev 	unsigned int device =
104*ca61a41fSAnton Yakovlev 		(defconf & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT;
105*ca61a41fSAnton Yakovlev 
106*ca61a41fSAnton Yakovlev 	switch (device) {
107*ca61a41fSAnton Yakovlev 	case AC_JACK_LINE_OUT:
108*ca61a41fSAnton Yakovlev 	case AC_JACK_SPEAKER:
109*ca61a41fSAnton Yakovlev 		return SND_JACK_LINEOUT;
110*ca61a41fSAnton Yakovlev 	case AC_JACK_HP_OUT:
111*ca61a41fSAnton Yakovlev 		return SND_JACK_HEADPHONE;
112*ca61a41fSAnton Yakovlev 	case AC_JACK_SPDIF_OUT:
113*ca61a41fSAnton Yakovlev 	case AC_JACK_DIG_OTHER_OUT:
114*ca61a41fSAnton Yakovlev 		return SND_JACK_AVOUT;
115*ca61a41fSAnton Yakovlev 	case AC_JACK_MIC_IN:
116*ca61a41fSAnton Yakovlev 		return SND_JACK_MICROPHONE;
117*ca61a41fSAnton Yakovlev 	default:
118*ca61a41fSAnton Yakovlev 		return SND_JACK_LINEIN;
119*ca61a41fSAnton Yakovlev 	}
120*ca61a41fSAnton Yakovlev }
121*ca61a41fSAnton Yakovlev 
122*ca61a41fSAnton Yakovlev /**
123*ca61a41fSAnton Yakovlev  * virtsnd_jack_parse_cfg() - Parse the jack configuration.
124*ca61a41fSAnton Yakovlev  * @snd: VirtIO sound device.
125*ca61a41fSAnton Yakovlev  *
126*ca61a41fSAnton Yakovlev  * This function is called during initial device initialization.
127*ca61a41fSAnton Yakovlev  *
128*ca61a41fSAnton Yakovlev  * Context: Any context that permits to sleep.
129*ca61a41fSAnton Yakovlev  * Return: 0 on success, -errno on failure.
130*ca61a41fSAnton Yakovlev  */
virtsnd_jack_parse_cfg(struct virtio_snd * snd)131*ca61a41fSAnton Yakovlev int virtsnd_jack_parse_cfg(struct virtio_snd *snd)
132*ca61a41fSAnton Yakovlev {
133*ca61a41fSAnton Yakovlev 	struct virtio_device *vdev = snd->vdev;
134*ca61a41fSAnton Yakovlev 	struct virtio_snd_jack_info *info;
135*ca61a41fSAnton Yakovlev 	u32 i;
136*ca61a41fSAnton Yakovlev 	int rc;
137*ca61a41fSAnton Yakovlev 
138*ca61a41fSAnton Yakovlev 	virtio_cread_le(vdev, struct virtio_snd_config, jacks, &snd->njacks);
139*ca61a41fSAnton Yakovlev 	if (!snd->njacks)
140*ca61a41fSAnton Yakovlev 		return 0;
141*ca61a41fSAnton Yakovlev 
142*ca61a41fSAnton Yakovlev 	snd->jacks = devm_kcalloc(&vdev->dev, snd->njacks, sizeof(*snd->jacks),
143*ca61a41fSAnton Yakovlev 				  GFP_KERNEL);
144*ca61a41fSAnton Yakovlev 	if (!snd->jacks)
145*ca61a41fSAnton Yakovlev 		return -ENOMEM;
146*ca61a41fSAnton Yakovlev 
147*ca61a41fSAnton Yakovlev 	info = kcalloc(snd->njacks, sizeof(*info), GFP_KERNEL);
148*ca61a41fSAnton Yakovlev 	if (!info)
149*ca61a41fSAnton Yakovlev 		return -ENOMEM;
150*ca61a41fSAnton Yakovlev 
151*ca61a41fSAnton Yakovlev 	rc = virtsnd_ctl_query_info(snd, VIRTIO_SND_R_JACK_INFO, 0, snd->njacks,
152*ca61a41fSAnton Yakovlev 				    sizeof(*info), info);
153*ca61a41fSAnton Yakovlev 	if (rc)
154*ca61a41fSAnton Yakovlev 		goto on_exit;
155*ca61a41fSAnton Yakovlev 
156*ca61a41fSAnton Yakovlev 	for (i = 0; i < snd->njacks; ++i) {
157*ca61a41fSAnton Yakovlev 		struct virtio_jack *vjack = &snd->jacks[i];
158*ca61a41fSAnton Yakovlev 
159*ca61a41fSAnton Yakovlev 		vjack->nid = le32_to_cpu(info[i].hdr.hda_fn_nid);
160*ca61a41fSAnton Yakovlev 		vjack->features = le32_to_cpu(info[i].features);
161*ca61a41fSAnton Yakovlev 		vjack->defconf = le32_to_cpu(info[i].hda_reg_defconf);
162*ca61a41fSAnton Yakovlev 		vjack->caps = le32_to_cpu(info[i].hda_reg_caps);
163*ca61a41fSAnton Yakovlev 		vjack->connected = info[i].connected;
164*ca61a41fSAnton Yakovlev 	}
165*ca61a41fSAnton Yakovlev 
166*ca61a41fSAnton Yakovlev on_exit:
167*ca61a41fSAnton Yakovlev 	kfree(info);
168*ca61a41fSAnton Yakovlev 
169*ca61a41fSAnton Yakovlev 	return rc;
170*ca61a41fSAnton Yakovlev }
171*ca61a41fSAnton Yakovlev 
172*ca61a41fSAnton Yakovlev /**
173*ca61a41fSAnton Yakovlev  * virtsnd_jack_build_devs() - Build ALSA controls for jacks.
174*ca61a41fSAnton Yakovlev  * @snd: VirtIO sound device.
175*ca61a41fSAnton Yakovlev  *
176*ca61a41fSAnton Yakovlev  * Context: Any context that permits to sleep.
177*ca61a41fSAnton Yakovlev  * Return: 0 on success, -errno on failure.
178*ca61a41fSAnton Yakovlev  */
virtsnd_jack_build_devs(struct virtio_snd * snd)179*ca61a41fSAnton Yakovlev int virtsnd_jack_build_devs(struct virtio_snd *snd)
180*ca61a41fSAnton Yakovlev {
181*ca61a41fSAnton Yakovlev 	u32 i;
182*ca61a41fSAnton Yakovlev 	int rc;
183*ca61a41fSAnton Yakovlev 
184*ca61a41fSAnton Yakovlev 	for (i = 0; i < snd->njacks; ++i) {
185*ca61a41fSAnton Yakovlev 		struct virtio_jack *vjack = &snd->jacks[i];
186*ca61a41fSAnton Yakovlev 
187*ca61a41fSAnton Yakovlev 		vjack->type = virtsnd_jack_get_type(vjack);
188*ca61a41fSAnton Yakovlev 
189*ca61a41fSAnton Yakovlev 		rc = snd_jack_new(snd->card, virtsnd_jack_get_label(vjack),
190*ca61a41fSAnton Yakovlev 				  vjack->type, &vjack->jack, true, true);
191*ca61a41fSAnton Yakovlev 		if (rc)
192*ca61a41fSAnton Yakovlev 			return rc;
193*ca61a41fSAnton Yakovlev 
194*ca61a41fSAnton Yakovlev 		if (vjack->jack)
195*ca61a41fSAnton Yakovlev 			vjack->jack->private_data = vjack;
196*ca61a41fSAnton Yakovlev 
197*ca61a41fSAnton Yakovlev 		snd_jack_report(vjack->jack,
198*ca61a41fSAnton Yakovlev 				vjack->connected ? vjack->type : 0);
199*ca61a41fSAnton Yakovlev 	}
200*ca61a41fSAnton Yakovlev 
201*ca61a41fSAnton Yakovlev 	return 0;
202*ca61a41fSAnton Yakovlev }
203*ca61a41fSAnton Yakovlev 
204*ca61a41fSAnton Yakovlev /**
205*ca61a41fSAnton Yakovlev  * virtsnd_jack_event() - Handle the jack event notification.
206*ca61a41fSAnton Yakovlev  * @snd: VirtIO sound device.
207*ca61a41fSAnton Yakovlev  * @event: VirtIO sound event.
208*ca61a41fSAnton Yakovlev  *
209*ca61a41fSAnton Yakovlev  * Context: Interrupt context.
210*ca61a41fSAnton Yakovlev  */
virtsnd_jack_event(struct virtio_snd * snd,struct virtio_snd_event * event)211*ca61a41fSAnton Yakovlev void virtsnd_jack_event(struct virtio_snd *snd, struct virtio_snd_event *event)
212*ca61a41fSAnton Yakovlev {
213*ca61a41fSAnton Yakovlev 	u32 jack_id = le32_to_cpu(event->data);
214*ca61a41fSAnton Yakovlev 	struct virtio_jack *vjack;
215*ca61a41fSAnton Yakovlev 
216*ca61a41fSAnton Yakovlev 	if (jack_id >= snd->njacks)
217*ca61a41fSAnton Yakovlev 		return;
218*ca61a41fSAnton Yakovlev 
219*ca61a41fSAnton Yakovlev 	vjack = &snd->jacks[jack_id];
220*ca61a41fSAnton Yakovlev 
221*ca61a41fSAnton Yakovlev 	switch (le32_to_cpu(event->hdr.code)) {
222*ca61a41fSAnton Yakovlev 	case VIRTIO_SND_EVT_JACK_CONNECTED:
223*ca61a41fSAnton Yakovlev 		vjack->connected = true;
224*ca61a41fSAnton Yakovlev 		break;
225*ca61a41fSAnton Yakovlev 	case VIRTIO_SND_EVT_JACK_DISCONNECTED:
226*ca61a41fSAnton Yakovlev 		vjack->connected = false;
227*ca61a41fSAnton Yakovlev 		break;
228*ca61a41fSAnton Yakovlev 	default:
229*ca61a41fSAnton Yakovlev 		return;
230*ca61a41fSAnton Yakovlev 	}
231*ca61a41fSAnton Yakovlev 
232*ca61a41fSAnton Yakovlev 	snd_jack_report(vjack->jack, vjack->connected ? vjack->type : 0);
233*ca61a41fSAnton Yakovlev }
234