Re: [RFC PATCH v2 07/22] ASoC: Add SOC USB APIs for adding an USB backend

From: Wesley Cheng
Date: Mon Jan 30 2023 - 17:36:46 EST


Hi Pierre,

On 1/26/2023 7:32 AM, Pierre-Louis Bossart wrote:


On 1/25/23 21:14, Wesley Cheng wrote:
Some platforms may want to register its USB port to be handled by the ASoC
framework. Audio playback/capture support is also handled entirely by the
vendor ASoC drivers.

Can you clarify what you mean by 'port'?


Ideally, port is intended to represent how many USB audio devices the audio DSP can support.

+/**
+ * snd_soc_usb_add_port() - Add a USB backend port
+ * @dev: USB backend device
+ * @connection_cb: connection status callback
+ *
+ * Register a USB backend device to the SND USB SOC framework. Memory is
+ * allocated as part of the USB backend device.
+ *
+ */
+struct snd_soc_usb *snd_soc_usb_add_port(struct device *dev,
+ int (*connection_cb)(struct snd_soc_usb *usb, int card_idx,
+ int connected))
+{
+ struct snd_soc_usb *usb;
+
+ usb = devm_kzalloc(dev, sizeof(*usb), GFP_KERNEL);
+ if (!usb)
+ return ERR_PTR(-ENOMEM);
+
+ usb->connection_status_cb = connection_cb;
+ usb->dev = dev;
+
+ mutex_lock(&ctx_mutex);
+ list_add_tail(&usb->list, &usb_ctx_list);
+ mutex_unlock(&ctx_mutex);
+
+ return usb;
+}
+EXPORT_SYMBOL_GPL(snd_soc_usb_add_port);

Can a backend have more than one ports?


The intended model is one BE per usb device.

Is there any relationship between port and USB endpoint, and if yes
where is this determined?


Might need some more clarification on this question. I mean, whichever port the USB device is connected to will be the USB endpoint(s) being utilized.

Maybe the confusion is in the "port" label itself? You can think of port meaning the same thing as a udev. (struct usb_device)

+
+/**
+ * snd_soc_usb_remove_port() - Remove a USB backend port
+ * @dev: USB backend device
+ *
+ * Remove a USB backend device from USB SND SOC. Memory is freed when USB
+ * backend is removed.
+ *
+ */
+int snd_soc_usb_remove_port(struct device *dev)
+{
+ struct snd_soc_usb *ctx, *tmp;
+
+ mutex_lock(&ctx_mutex);
+ list_for_each_entry_safe(ctx, tmp, &usb_ctx_list, list) {
+ if (ctx->dev == dev) {
+ list_del(&ctx->list);
+ break;
+ }
+ }
+ mutex_unlock(&ctx_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_usb_remove_port);

What happens if the ASoC driver probes/initialize AFTER the USB device
is plugged?


Good point, that's one thing I was seeing how we could address initially, but never got around to adding it. Currently the code would basically not enable the DAPM pins/path for that backend device. (when we probe the USB backend, we disable the USB_RX_BE pin)

If the USB SND driver gets the connection before the USB BE is up, then q6usb_alsa_connection_cb() would not be called, which sets the USB_RX_BE pin state (to enable). Then when the Q6USB backend is probed, the USB_RX_BE pin state would be set to disabled.

Will see if I can maybe cache the connection state somewhere and pass it along when the USB BE is up.

Likewise, can the ASoC driver be removed 'safely' with a fallback to
normal non-offloaded operation happening on remove?


As of now, there is no automatic fallback for that scenario. If I compile all our Q6 dai drivers as modules as well as the platform card, then we won't be able to individually remove ASoC component modules since they are being used by the platform soundcard device.

The only way to remove the USB backend driver is first to remove the platform sound card, which will tear down the current audio session and remove the sound card that was created.


+
+/**
+ * snd_soc_usb_connect() - Notification of USB device connection
+ * @usbdev: USB bus device
+ * @card_idx: USB SND card instance
+ *
+ * Notify of a new USB SND device connection. The card_idx can be used to
+ * handle how the USB backend selects, which device to enable offloading on.
+ *
+ */
+int snd_soc_usb_connect(struct device *usbdev, int card_idx)
+{
+ struct snd_soc_usb *ctx;
+
+ if (!usbdev)
+ return -ENODEV;
+
+ ctx = snd_soc_find_usb_ctx(usbdev);
+ if (!ctx)
+ return -ENODEV;
+
+ if (ctx->connection_status_cb)
+ ctx->connection_status_cb(ctx, card_idx, 1);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_usb_connect);
+
+/**
+ * snd_soc_usb_connect() - Notification of USB device connection
+ * @usbdev: USB bus device
+ *
+ * Notify of a new USB SND device disconnection to the USB backend.
+ *
+ */
+int snd_soc_usb_disconnect(struct device *usbdev)
+{
+ struct snd_soc_usb *ctx;
+
+ if (!usbdev)
+ return -ENODEV;
+
+ ctx = snd_soc_find_usb_ctx(usbdev);
+ if (!ctx)
+ return -ENODEV;
+
+ if (ctx->connection_status_cb)
+ ctx->connection_status_cb(ctx, -1, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_usb_disconnect);

Similar concern on connect/disconnect, does this assume any specific
order for the driver probe?

I think the above explanation clarifies the order which is currently going to cause us to potentially miss a device connection.

Thanks
Wesley Cheng