1
2
3
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
13static DEFINE_MUTEX(ctx_mutex);
14static LIST_HEAD(usb_ctx_list);
15
16static 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
27static 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
42static 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
59
60
61
62
63
64
65
66
67
68
69
70int 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}
91EXPORT_SYMBOL_GPL(snd_soc_usb_setup_offload_jack);
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108int 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);
123exit:
124 mutex_unlock(&ctx_mutex);
125
126 return ret;
127}
128EXPORT_SYMBOL_GPL(snd_soc_usb_update_offload_route);
129
130
131
132
133
134
135
136
137void *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}
147EXPORT_SYMBOL_GPL(snd_soc_usb_find_priv_data);
148
149
150
151
152
153
154
155
156
157
158
159
160
161int 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}
173EXPORT_SYMBOL_GPL(snd_soc_usb_find_supported_format);
174
175
176
177
178
179
180
181
182
183
184
185
186
187struct 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}
201EXPORT_SYMBOL_GPL(snd_soc_usb_allocate_port);
202
203
204
205
206
207
208
209
210
211void snd_soc_usb_free_port(struct snd_soc_usb *usb)
212{
213 snd_soc_usb_remove_port(usb);
214 kfree(usb);
215}
216EXPORT_SYMBOL_GPL(snd_soc_usb_free_port);
217
218
219
220
221
222
223
224
225
226void 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}
234EXPORT_SYMBOL_GPL(snd_soc_usb_add_port);
235
236
237
238
239
240
241
242
243
244void 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}
257EXPORT_SYMBOL_GPL(snd_soc_usb_remove_port);
258
259
260
261
262
263
264
265
266
267
268
269int 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
284exit:
285 mutex_unlock(&ctx_mutex);
286
287 return 0;
288}
289EXPORT_SYMBOL_GPL(snd_soc_usb_connect);
290
291
292
293
294
295
296
297
298
299int 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
314exit:
315 mutex_unlock(&ctx_mutex);
316
317 return 0;
318}
319EXPORT_SYMBOL_GPL(snd_soc_usb_disconnect);
320
321MODULE_LICENSE("GPL");
322MODULE_DESCRIPTION("SoC USB driver for offloading");
323