Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/virtio/virtio_jack.c
26378 views
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
}
234
235