Hi Pierre, On 9/25/2024 7:43 AM, Pierre-Louis Bossart wrote: >> + int snd_soc_usb_setup_offload_jack(struct snd_soc_component *component, >> + struct snd_soc_jack *jack) >> +.. >> + >> + - ``component``: ASoC component to add the jack >> + - ``jack``: jack component to populate >> + >> +**snd_soc_usb_setup_offload_jack()** is a helper to add a sound jack control to >> +the platform sound card. This will allow for consistent naming to be used on >> +designs that support USB audio offloading. >> + >> +Returns 0 on success, negative otherwise. >> + >> +.. code-block:: rst >> + >> + int snd_soc_usb_disable_offload_jack(struct snd_soc_component *component) >> +.. >> + >> + - ``component``: ASoC component to disable the jack >> + >> +**snd_soc_usb_disable_offload_jack()** is a helper to disable a sound jack control >> +on the platform sound card. > is disable_offload_jack() the companion operation to setup_offload_jack()? > > it's not clear to me if there's any relationship between the two. I guess there is a relation in that one creates the jack and the other will disable it when needed. Might need to have a respective enable API, because I believe there are some situations during PM suspend where a jack may want to be disabled and re-enabled on PM resume. >> + >> +Returns 0 on success, negative otherwise. >> + >> +.. code-block:: rst >> + >> + int snd_soc_usb_update_offload_route(struct device *dev, int card, int pcm, >> + int direction, long *route) >> +.. >> + >> + - ``dev``: USB device to look up offload path mapping >> + - ``card``: USB sound card index >> + - ``pcm``: USB sound PCM device index >> + - ``direction``: direction to fetch offload routing information >> + - ``route``: mapping of sound card and pcm indexes for the offload path. This is >> + an array of two integers that will carry the card and pcm device indexes >> + in that specific order. This can be used as the array for the kcontrol >> + output. >> + >> +**snd_soc_usb_update_offload_route()** calls a registered callback to the USB BE DAI >> +link to fetch the information about the mapped ASoC devices for executing USB audio >> +offload for the device. ``route`` may be a pointer to a kcontrol value output array, >> +which carries values when the kcontrol is read. >> + >> +Returns 0 on success, negative otherwise. > please clarify what happens if there is no offloaded device for a > specific USB card. from [2] below it looks like the intended behavior > for a device without offload capabilities would be to return 0 but the > mapping would use the -1 magic value to state there is no offload? > That is the idea... If we return -1,-1 that is an invalid card/pcm device index, so it would signify that offloading is not available for a USB device. >> +**snd_soc_usb_free_port()** frees a SOC USB device. >> + >> +.. code-block:: rst >> + >> + void snd_soc_usb_add_port(struct snd_soc_usb *usb); >> +.. >> + >> + - ``usb``: SOC USB device to add >> + >> +**snd_soc_usb_add_port()** add an allocated SOC USB device to the SOC USB framework. >> +Once added, this device can be referenced by further operations. >> + >> +.. code-block:: rst >> + >> + void snd_soc_usb_remove_port(struct snd_soc_usb *usb); >> +.. >> + >> + - ``usb``: SOC USB device to remove >> + >> +**snd_soc_usb_remove_port()** removes a SOC USB device from the SOC USB framework. >> +After removing a device, any SOC USB operations would not be able to reference the >> +device removed. > I don't think the last sentence is correct, below [1] you show an > example where the free_port() routine is required... > The remove will remove it from the available list of SOC USB ports. The free will just make sure the memory allocated for the SOC USB port is freed. >> + >> + static void q6usb_component_remove(struct snd_soc_component *component) >> + { >> + ... > [1] > >> + snd_soc_usb_remove_port(data->usb); >> + snd_soc_usb_free_port(data->usb); >> + } >> + >> + static const struct snd_soc_component_driver q6usb_dai_component = { >> + .probe = q6usb_component_probe, >> + .remove = q6usb_component_remove, >> + .name = "q6usb-dai-component", >> + ... >> + }; >> +.. >> + >> +BE DAI links can pass along vendor specific information as part of the >> +call to allocate the SOC USB device. This will allow any BE DAI link >> +parameters or settings to be accessed by the USB offload driver that >> +resides in USB SND. >> + >> +USB Audio Device Connection Flow >> +-------------------------------- >> +USB devices can be hotplugged into the USB ports at any point in time. >> +The BE DAI link should be aware of the current state of the physical USB >> +port, i.e. if there are any USB devices with audio interface(s) connected. >> +connection_status_cb() can be used to notify the BE DAI link of any change. >> + >> +This is called whenever there is a USB SND interface bind or remove event, >> +using snd_soc_usb_connect() or snd_soc_usb_disconnect(): >> + >> +.. code-block:: rst >> + >> + static void qc_usb_audio_offload_probe(struct snd_usb_audio *chip) >> + { >> + ... >> + snd_soc_usb_connect(usb_get_usb_backend(udev), sdev); >> + ... >> + } >> + >> + static void qc_usb_audio_offload_disconnect(struct snd_usb_audio *chip) >> + { >> + ... >> + snd_soc_usb_disconnect(usb_get_usb_backend(chip->dev), dev->sdev); >> + ... >> + } >> +.. >> + >> +In order to account for conditions where driver or device existence is >> +not guaranteed, USB SND exposes snd_usb_rediscover_devices() to resend the >> +connect events for any identified USB audio interfaces. Consider the >> +the following situation: >> + >> + **usb_audio_probe()** >> + | --> USB audio streams allocated and saved to usb_chip[] >> + | --> Propagate connect event to USB offload driver in USB SND >> + | --> **snd_soc_usb_connect()** exits as USB BE DAI link is not ready >> + >> + BE DAI link component probe >> + | --> DAI link is probed and SOC USB port is allocated >> + | --> The USB audio device connect event is missed >> + >> +To ensure connection events are not missed, **snd_usb_rediscover_devices()** >> +is executed when the SOC USB device is registered. Now, when the BE DAI >> +link component probe occurs, the following highlights the sequence: >> + >> + BE DAI link component probe >> + | --> DAI link is probed and SOC USB port is allocated >> + | --> SOC USB device added, and **snd_usb_rediscover_devices()** runs >> + >> + **snd_usb_rediscover_devices()** >> + | --> Traverses through usb_chip[] and for non-NULL entries issue >> + | **connection_status_cb()** >> + >> +In the case where the USB offload driver is unbound, while USB SND is ready, >> +the **snd_usb_rediscover_devices()** is called during module init. This allows >> +for the offloading path to also be enabled with the following flow: >> + >> + **usb_audio_probe()** >> + | --> USB audio streams allocated and saved to usb_chip[] >> + | --> Propagate connect event to USB offload driver in USB SND >> + | --> USB offload driver **NOT** ready! >> + >> + BE DAI link component probe >> + | --> DAI link is probed and SOC USB port is allocated >> + | --> No USB connect event due to missing USB offload driver >> + >> + USB offload driver probe >> + | --> **qc_usb_audio_offload_init()** >> + | --> Calls **snd_usb_rediscover_devices()** to notify of devices >> + >> +USB Offload Related Kcontrols >> +============================= >> +Details >> +------- >> +A set of kcontrols can be utilized by applications to help select the proper sound >> +devices to enable USB audio offloading. SOC USB exposes the get_offload_dev() >> +callback that designs can use to ensure that the proper indices are returned to the >> +application. >> + >> +Implementation >> +-------------- >> + >> +**Example:** >> + >> + **Sound Cards**: >> + >> + :: >> + >> + 0 [SM8250MTPWCD938]: sm8250 - SM8250-MTP-WCD9380-WSA8810-VA-D >> + SM8250-MTP-WCD9380-WSA8810-VA-DMIC >> + 1 [Seri ]: USB-Audio - Plantronics Blackwire 3225 Seri >> + Plantronics Plantronics Blackwire >> + 3225 Seri at usb-xhci-hcd.1.auto-1.1, >> + full sp >> + 2 [C320M ]: USB-Audio - Plantronics C320-M >> + Plantronics Plantronics C320-M at usb-xhci-hcd.1.auto-1.2, full speed >> + >> + **USB Sound Card** - card#1: >> + >> + :: >> + >> + USB Offload Playback Route PCM#0 -1, -1 (range -1->255) >> + >> + **USB Sound Card** - card#2: >> + >> + :: >> + >> + USB Offload Playback Route PCM#0 0, 1 (range -1->255) >> + >> +The above example shows a scenario where the system has one ASoC platform card >> +(card#0) and two USB sound devices connected (card#1 and card#2). When reading >> +the available kcontrols for each USB audio device, the following kcontrol lists >> +the mapped offload path for the specific device: >> + >> + ``USB Offload Playback Route#*`` >> + >> +The kcontrol is indexed, because a USB audio device could potentially have >> +several PCM devices. The above kcontrols are defined as: >> + >> + - ``USB Offload Playback Route PCM`` **(R)**: Returns the ASoC platform sound >> + card and PCM device index. The output **"0, 1"** (card index, PCM device index) >> + signifies that there is an available offload path for the USB SND device >> + through card#0 - PCM device#1. If **"-1, -1"** is seen, then no offload path is >> + available for the USB SND device. > [2] > > maybe I got this wrong but you may want to clarify that the kcontrol is > always created, but the values indicate if the offload support is real > or not? Sure, I will explicitly mention that the kcontrol always exists, and if the path is not available, then this would show -1, -1 > >> + >> +USB Offload Playback Route Kcontrol >> +----------------------------------- >> +In order to allow for vendor specific implementations on audio offloading device >> +selection, the SOC USB layer exposes the following: >> + >> +.. code-block:: rst >> + >> + int (*update_offload_route_info)(struct snd_soc_component *component, >> + int card, int pcm, long *route); >> +.. >> + >> +These are specific for the **USB Offload Playback Route PCM#** kcontrol. >> + >> +When users issue get calls to the kcontrol, the registered SOC USB callbacks will >> +execute the registered function calls to the DPCM BE DAI link. >> + >> +**Callback Registration:** >> + >> +.. code-block:: rst >> + >> + static int q6usb_component_probe(struct snd_soc_component *component) >> + { >> + ... >> + usb = snd_soc_usb_allocate_port(component, 1, &data->priv); >> + if (IS_ERR(usb)) >> + return -ENOMEM; >> + >> + usb->connection_status_cb = q6usb_alsa_connection_cb; >> + usb->update_offload_route_info = q6usb_get_offload_dev; >> + >> + ret = snd_soc_usb_add_port(usb); >> +.. >> + >> +Existing USB Sound Kcontrol >> +--------------------------- >> +With the introduction of USB offload support, the above USB offload kcontrol >> +can be added to the pre existing list of kcontrols identified by the USB sound > is this 'can be added' or 'will be added'? The latter seems more correct > to me, I don't see anything optional or conditional in the description > and the example below. Will be added sounds better. Will change that. Thanks Wesley Cheng >> +framework. These kcontrols are still the main controls that are used to >> +modify characteristics pertaining to the USB audio device. >> + >> + :: >> + >> + Number of controls: 9 >> + ctl type num name value >> + 0 INT 2 Capture Channel Map 0, 0 (range 0->36) >> + 1 INT 2 Playback Channel Map 0, 0 (range 0->36) >> + 2 BOOL 1 Headset Capture Switch On >> + 3 INT 1 Headset Capture Volume 10 (range 0->13) >> + 4 BOOL 1 Sidetone Playback Switch On >> + 5 INT 1 Sidetone Playback Volume 4096 (range 0->8192) >> + 6 BOOL 1 Headset Playback Switch On >> + 7 INT 2 Headset Playback Volume 20, 20 (range 0->24) >> + 8 INT 2 USB Offload Playback Route PCM#0 -1, -1 (range -1->255) >> + >> +Since USB audio device controls are handled over the USB control endpoint, use the >> +existing mechanisms present in the USB mixer to set parameters, such as volume.