Merge tag 'hyperv-next-signed-20210831' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / sound / virtio / virtio_jack.c
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  */
32 struct 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  */
52 static 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  */
100 static 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  */
131 int 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
166 on_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  */
179 int 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  */
211 void 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 }