Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/soc-usb.c
26378 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
4
*/
5
#include <linux/of.h>
6
#include <linux/usb.h>
7
8
#include <sound/jack.h>
9
#include <sound/soc-usb.h>
10
11
#include "../usb/card.h"
12
13
static DEFINE_MUTEX(ctx_mutex);
14
static LIST_HEAD(usb_ctx_list);
15
16
static struct device_node *snd_soc_find_phandle(struct device *dev)
17
{
18
struct device_node *node;
19
20
node = of_parse_phandle(dev->of_node, "usb-soc-be", 0);
21
if (!node)
22
return ERR_PTR(-ENODEV);
23
24
return node;
25
}
26
27
static struct snd_soc_usb *snd_soc_usb_ctx_lookup(struct device_node *node)
28
{
29
struct snd_soc_usb *ctx;
30
31
if (!node)
32
return NULL;
33
34
list_for_each_entry(ctx, &usb_ctx_list, list) {
35
if (ctx->component->dev->of_node == node)
36
return ctx;
37
}
38
39
return NULL;
40
}
41
42
static struct snd_soc_usb *snd_soc_find_usb_ctx(struct device *dev)
43
{
44
struct snd_soc_usb *ctx;
45
struct device_node *node;
46
47
node = snd_soc_find_phandle(dev);
48
if (!IS_ERR(node)) {
49
ctx = snd_soc_usb_ctx_lookup(node);
50
of_node_put(node);
51
} else {
52
ctx = snd_soc_usb_ctx_lookup(dev->of_node);
53
}
54
55
return ctx ? ctx : NULL;
56
}
57
58
/* SOC USB sound kcontrols */
59
/**
60
* snd_soc_usb_setup_offload_jack() - Create USB offloading jack
61
* @component: USB DPCM backend DAI component
62
* @jack: jack structure to create
63
*
64
* Creates a jack device for notifying userspace of the availability
65
* of an offload capable device.
66
*
67
* Returns 0 on success, negative on error.
68
*
69
*/
70
int snd_soc_usb_setup_offload_jack(struct snd_soc_component *component,
71
struct snd_soc_jack *jack)
72
{
73
int ret;
74
75
ret = snd_soc_card_jack_new(component->card, "USB Offload Jack",
76
SND_JACK_USB, jack);
77
if (ret < 0) {
78
dev_err(component->card->dev, "Unable to add USB offload jack: %d\n",
79
ret);
80
return ret;
81
}
82
83
ret = snd_soc_component_set_jack(component, jack, NULL);
84
if (ret) {
85
dev_err(component->card->dev, "Failed to set jack: %d\n", ret);
86
return ret;
87
}
88
89
return 0;
90
}
91
EXPORT_SYMBOL_GPL(snd_soc_usb_setup_offload_jack);
92
93
/**
94
* snd_soc_usb_update_offload_route - Find active USB offload path
95
* @dev: USB device to get offload status
96
* @card: USB card index
97
* @pcm: USB PCM device index
98
* @direction: playback or capture direction
99
* @path: pcm or card index
100
* @route: pointer to route output array
101
*
102
* Fetch the current status for the USB SND card and PCM device indexes
103
* specified. The "route" argument should be an array of integers being
104
* used for a kcontrol output. The first element should have the selected
105
* card index, and the second element should have the selected pcm device
106
* index.
107
*/
108
int snd_soc_usb_update_offload_route(struct device *dev, int card, int pcm,
109
int direction, enum snd_soc_usb_kctl path,
110
long *route)
111
{
112
struct snd_soc_usb *ctx;
113
int ret = -ENODEV;
114
115
mutex_lock(&ctx_mutex);
116
ctx = snd_soc_find_usb_ctx(dev);
117
if (!ctx)
118
goto exit;
119
120
if (ctx->update_offload_route_info)
121
ret = ctx->update_offload_route_info(ctx->component, card, pcm,
122
direction, path, route);
123
exit:
124
mutex_unlock(&ctx_mutex);
125
126
return ret;
127
}
128
EXPORT_SYMBOL_GPL(snd_soc_usb_update_offload_route);
129
130
/**
131
* snd_soc_usb_find_priv_data() - Retrieve private data stored
132
* @usbdev: device reference
133
*
134
* Fetch the private data stored in the USB SND SoC structure.
135
*
136
*/
137
void *snd_soc_usb_find_priv_data(struct device *usbdev)
138
{
139
struct snd_soc_usb *ctx;
140
141
mutex_lock(&ctx_mutex);
142
ctx = snd_soc_find_usb_ctx(usbdev);
143
mutex_unlock(&ctx_mutex);
144
145
return ctx ? ctx->priv_data : NULL;
146
}
147
EXPORT_SYMBOL_GPL(snd_soc_usb_find_priv_data);
148
149
/**
150
* snd_soc_usb_find_supported_format() - Check if audio format is supported
151
* @card_idx: USB sound chip array index
152
* @params: PCM parameters
153
* @direction: capture or playback
154
*
155
* Ensure that a requested audio profile from the ASoC side is able to be
156
* supported by the USB device.
157
*
158
* Return 0 on success, negative on error.
159
*
160
*/
161
int snd_soc_usb_find_supported_format(int card_idx,
162
struct snd_pcm_hw_params *params,
163
int direction)
164
{
165
struct snd_usb_stream *as;
166
167
as = snd_usb_find_suppported_substream(card_idx, params, direction);
168
if (!as)
169
return -EOPNOTSUPP;
170
171
return 0;
172
}
173
EXPORT_SYMBOL_GPL(snd_soc_usb_find_supported_format);
174
175
/**
176
* snd_soc_usb_allocate_port() - allocate a SoC USB port for offloading support
177
* @component: USB DPCM backend DAI component
178
* @data: private data
179
*
180
* Allocate and initialize a SoC USB port. The SoC USB port is used to communicate
181
* different USB audio devices attached, in order to start audio offloading handled
182
* by an ASoC entity. USB device plug in/out events are signaled with a
183
* notification, but don't directly impact the memory allocated for the SoC USB
184
* port.
185
*
186
*/
187
struct snd_soc_usb *snd_soc_usb_allocate_port(struct snd_soc_component *component,
188
void *data)
189
{
190
struct snd_soc_usb *usb;
191
192
usb = kzalloc(sizeof(*usb), GFP_KERNEL);
193
if (!usb)
194
return ERR_PTR(-ENOMEM);
195
196
usb->component = component;
197
usb->priv_data = data;
198
199
return usb;
200
}
201
EXPORT_SYMBOL_GPL(snd_soc_usb_allocate_port);
202
203
/**
204
* snd_soc_usb_free_port() - free a SoC USB port used for offloading support
205
* @usb: allocated SoC USB port
206
*
207
* Free and remove the SoC USB port from the available list of ports. This will
208
* ensure that the communication between USB SND and ASoC is halted.
209
*
210
*/
211
void snd_soc_usb_free_port(struct snd_soc_usb *usb)
212
{
213
snd_soc_usb_remove_port(usb);
214
kfree(usb);
215
}
216
EXPORT_SYMBOL_GPL(snd_soc_usb_free_port);
217
218
/**
219
* snd_soc_usb_add_port() - Add a USB backend port
220
* @usb: soc usb port to add
221
*
222
* Register a USB backend DAI link to the USB SoC framework. Memory is allocated
223
* as part of the USB backend DAI link.
224
*
225
*/
226
void snd_soc_usb_add_port(struct snd_soc_usb *usb)
227
{
228
mutex_lock(&ctx_mutex);
229
list_add_tail(&usb->list, &usb_ctx_list);
230
mutex_unlock(&ctx_mutex);
231
232
snd_usb_rediscover_devices();
233
}
234
EXPORT_SYMBOL_GPL(snd_soc_usb_add_port);
235
236
/**
237
* snd_soc_usb_remove_port() - Remove a USB backend port
238
* @usb: soc usb port to remove
239
*
240
* Remove a USB backend DAI link from USB SoC. Memory is freed when USB backend
241
* DAI is removed, or when snd_soc_usb_free_port() is called.
242
*
243
*/
244
void snd_soc_usb_remove_port(struct snd_soc_usb *usb)
245
{
246
struct snd_soc_usb *ctx, *tmp;
247
248
mutex_lock(&ctx_mutex);
249
list_for_each_entry_safe(ctx, tmp, &usb_ctx_list, list) {
250
if (ctx == usb) {
251
list_del(&ctx->list);
252
break;
253
}
254
}
255
mutex_unlock(&ctx_mutex);
256
}
257
EXPORT_SYMBOL_GPL(snd_soc_usb_remove_port);
258
259
/**
260
* snd_soc_usb_connect() - Notification of USB device connection
261
* @usbdev: USB bus device
262
* @sdev: USB SND device to add
263
*
264
* Notify of a new USB SND device connection. The sdev->card_idx can be used to
265
* handle how the DPCM backend selects, which device to enable USB offloading
266
* on.
267
*
268
*/
269
int snd_soc_usb_connect(struct device *usbdev, struct snd_soc_usb_device *sdev)
270
{
271
struct snd_soc_usb *ctx;
272
273
if (!usbdev)
274
return -ENODEV;
275
276
mutex_lock(&ctx_mutex);
277
ctx = snd_soc_find_usb_ctx(usbdev);
278
if (!ctx)
279
goto exit;
280
281
if (ctx->connection_status_cb)
282
ctx->connection_status_cb(ctx, sdev, true);
283
284
exit:
285
mutex_unlock(&ctx_mutex);
286
287
return 0;
288
}
289
EXPORT_SYMBOL_GPL(snd_soc_usb_connect);
290
291
/**
292
* snd_soc_usb_disconnect() - Notification of USB device disconnection
293
* @usbdev: USB bus device
294
* @sdev: USB SND device to remove
295
*
296
* Notify of a new USB SND device disconnection to the USB backend.
297
*
298
*/
299
int snd_soc_usb_disconnect(struct device *usbdev, struct snd_soc_usb_device *sdev)
300
{
301
struct snd_soc_usb *ctx;
302
303
if (!usbdev)
304
return -ENODEV;
305
306
mutex_lock(&ctx_mutex);
307
ctx = snd_soc_find_usb_ctx(usbdev);
308
if (!ctx)
309
goto exit;
310
311
if (ctx->connection_status_cb)
312
ctx->connection_status_cb(ctx, sdev, false);
313
314
exit:
315
mutex_unlock(&ctx_mutex);
316
317
return 0;
318
}
319
EXPORT_SYMBOL_GPL(snd_soc_usb_disconnect);
320
321
MODULE_LICENSE("GPL");
322
MODULE_DESCRIPTION("SoC USB driver for offloading");
323
324