File Index
Files and directories
Sign in
FILE

soc-usb.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/soc-usb.c C
  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
 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/* 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 */
 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 * 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 */
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 * 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 */
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 * 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 */
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 * 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 */
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 * 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 */
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 * 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 */
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 * 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 */
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 * 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 */
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 * 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 */
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");
~/ Read-only file preview.