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