mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-21 06:50:25 +00:00
483 lines
17 KiB
ReStructuredText
483 lines
17 KiB
ReStructuredText
![]() |
================
|
||
|
ASoC USB support
|
||
|
================
|
||
|
|
||
|
Overview
|
||
|
========
|
||
|
In order to leverage the existing USB sound device support in ALSA, the
|
||
|
ASoC USB APIs are introduced to allow the subsystems to exchange
|
||
|
configuration information.
|
||
|
|
||
|
One potential use case would be to support USB audio offloading, which is
|
||
|
an implementation that allows for an alternate power-optimized path in the audio
|
||
|
subsystem to handle the transfer of audio data over the USB bus. This would
|
||
|
let the main processor to stay in lower power modes for longer duration. The
|
||
|
following is an example design of how the ASoC and ALSA pieces can be connected
|
||
|
together to achieve this:
|
||
|
|
||
|
::
|
||
|
|
||
|
USB | ASoC
|
||
|
| _________________________
|
||
|
| | ASoC Platform card |
|
||
|
| |_________________________|
|
||
|
| | |
|
||
|
| ___V____ ____V____
|
||
|
| |ASoC BE | |ASoC FE |
|
||
|
| |DAI LNK | |DAI LNK |
|
||
|
| |________| |_________|
|
||
|
| ^ ^ ^
|
||
|
| | |________|
|
||
|
| ___V____ |
|
||
|
| |SoC-USB | |
|
||
|
________ ________ | | |
|
||
|
|USB SND |<--->|USBSND |<------------>|________| |
|
||
|
|(card.c)| |offld |<---------- |
|
||
|
|________| |________|___ | | |
|
||
|
^ ^ | | | ____________V_________
|
||
|
| | | | | |IPC |
|
||
|
__ V_______________V_____ | | | |______________________|
|
||
|
|USB SND (endpoint.c) | | | | ^
|
||
|
|_________________________| | | | |
|
||
|
^ | | | ___________V___________
|
||
|
| | | |->|audio DSP |
|
||
|
___________V_____________ | | |_______________________|
|
||
|
|XHCI HCD |<- |
|
||
|
|_________________________| |
|
||
|
|
||
|
|
||
|
SoC USB driver
|
||
|
==============
|
||
|
Structures
|
||
|
----------
|
||
|
``struct snd_soc_usb``
|
||
|
|
||
|
- ``list``: list head for SND SoC struct list
|
||
|
- ``component``: reference to ASoC component
|
||
|
- ``connection_status_cb``: callback to notify connection events
|
||
|
- ``update_offload_route_info``: callback to fetch selected USB sound card/PCM
|
||
|
device
|
||
|
- ``priv_data``: driver data
|
||
|
|
||
|
The snd_soc_usb structure can be referenced using the ASoC platform card
|
||
|
device, or a USB device (udev->dev). This is created by the ASoC BE DAI
|
||
|
link, and the USB sound entity will be able to pass information to the
|
||
|
ASoC BE DAI link using this structure.
|
||
|
|
||
|
``struct snd_soc_usb_device``
|
||
|
|
||
|
- ``card_idx``: sound card index associated with USB sound device
|
||
|
- ``chip_idx``: USB sound chip array index
|
||
|
- ``cpcm_idx``: capture pcm device indexes associated with the USB sound device
|
||
|
- ``ppcm_idx``: playback pcm device indexes associated with the USB sound device
|
||
|
- ``num_playback``: number of playback streams
|
||
|
- ``num_capture``: number of capture streams
|
||
|
- ``list``: list head for the USB sound device list
|
||
|
|
||
|
The struct snd_soc_usb_device is created by the USB sound offload driver.
|
||
|
This will carry basic parameters/limitations that will be used to
|
||
|
determine the possible offloading paths for this USB audio device.
|
||
|
|
||
|
Functions
|
||
|
---------
|
||
|
.. code-block:: rst
|
||
|
|
||
|
int snd_soc_usb_find_supported_format(int card_idx,
|
||
|
struct snd_pcm_hw_params *params, int direction)
|
||
|
..
|
||
|
|
||
|
- ``card_idx``: the index into the USB sound chip array.
|
||
|
- ``params``: Requested PCM parameters from the USB DPCM BE DAI link
|
||
|
- ``direction``: capture or playback
|
||
|
|
||
|
**snd_soc_usb_find_supported_format()** ensures that the requested audio profile
|
||
|
being requested by the external DSP is supported by the USB device.
|
||
|
|
||
|
Returns 0 on success, and -EOPNOTSUPP on failure.
|
||
|
|
||
|
.. code-block:: rst
|
||
|
|
||
|
int snd_soc_usb_connect(struct device *usbdev, struct snd_soc_usb_device *sdev)
|
||
|
..
|
||
|
|
||
|
- ``usbdev``: the usb device that was discovered
|
||
|
- ``sdev``: capabilities of the device
|
||
|
|
||
|
**snd_soc_usb_connect()** notifies the ASoC USB DCPM BE DAI link of a USB
|
||
|
audio device detection. This can be utilized in the BE DAI
|
||
|
driver to keep track of available USB audio devices. This is intended
|
||
|
to be called by the USB offload driver residing in USB SND.
|
||
|
|
||
|
Returns 0 on success, negative error code on failure.
|
||
|
|
||
|
.. code-block:: rst
|
||
|
|
||
|
int snd_soc_usb_disconnect(struct device *usbdev, struct snd_soc_usb_device *sdev)
|
||
|
..
|
||
|
|
||
|
- ``usbdev``: the usb device that was removed
|
||
|
- ``sdev``: capabilities to free
|
||
|
|
||
|
**snd_soc_usb_disconnect()** notifies the ASoC USB DCPM BE DAI link of a USB
|
||
|
audio device removal. This is intended to be called by the USB offload
|
||
|
driver that resides in USB SND.
|
||
|
|
||
|
.. code-block:: rst
|
||
|
|
||
|
void *snd_soc_usb_find_priv_data(struct device *usbdev)
|
||
|
..
|
||
|
|
||
|
- ``usbdev``: the usb device to reference to find private data
|
||
|
|
||
|
**snd_soc_usb_find_priv_data()** fetches the private data saved to the SoC USB
|
||
|
device.
|
||
|
|
||
|
Returns pointer to priv_data on success, NULL on failure.
|
||
|
|
||
|
.. code-block:: rst
|
||
|
|
||
|
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. Additionally, this will enable the
|
||
|
jack to notify of changes.
|
||
|
|
||
|
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, enum snd_soc_usb_kctl path,
|
||
|
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
|
||
|
- ``path``: kcontrol selector - pcm device or card index
|
||
|
- ``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.
|
||
|
|
||
|
.. code-block:: rst
|
||
|
|
||
|
struct snd_soc_usb *snd_soc_usb_allocate_port(struct snd_soc_component *component,
|
||
|
void *data);
|
||
|
..
|
||
|
|
||
|
- ``component``: DPCM BE DAI link component
|
||
|
- ``data``: private data
|
||
|
|
||
|
**snd_soc_usb_allocate_port()** allocates a SoC USB device and populates standard
|
||
|
parameters that is used for further operations.
|
||
|
|
||
|
Returns a pointer to struct soc_usb on success, negative on error.
|
||
|
|
||
|
.. code-block:: rst
|
||
|
|
||
|
void snd_soc_usb_free_port(struct snd_soc_usb *usb);
|
||
|
..
|
||
|
|
||
|
- ``usb``: SoC USB device to free
|
||
|
|
||
|
**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.
|
||
|
|
||
|
How to Register to SoC USB
|
||
|
--------------------------
|
||
|
The ASoC DPCM USB BE DAI link is the entity responsible for allocating and
|
||
|
registering the SoC USB device on the component bind. Likewise, it will
|
||
|
also be responsible for freeing the allocated resources. An example can
|
||
|
be shown below:
|
||
|
|
||
|
.. code-block:: rst
|
||
|
|
||
|
static int q6usb_component_probe(struct snd_soc_component *component)
|
||
|
{
|
||
|
...
|
||
|
data->usb = snd_soc_usb_allocate_port(component, 1, &data->priv);
|
||
|
if (!data->usb)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
usb->connection_status_cb = q6usb_alsa_connection_cb;
|
||
|
|
||
|
ret = snd_soc_usb_add_port(usb);
|
||
|
if (ret < 0) {
|
||
|
dev_err(component->dev, "failed to add usb port\n");
|
||
|
goto free_usb;
|
||
|
}
|
||
|
...
|
||
|
}
|
||
|
|
||
|
static void q6usb_component_remove(struct snd_soc_component *component)
|
||
|
{
|
||
|
...
|
||
|
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
|
||
|
|
||
|
**PCM Devices**:
|
||
|
|
||
|
::
|
||
|
|
||
|
card 0: SM8250MTPWCD938 [SM8250-MTP-WCD9380-WSA8810-VA-D], device 0: MultiMedia1 (*) []
|
||
|
Subdevices: 1/1
|
||
|
Subdevice #0: subdevice #0
|
||
|
card 0: SM8250MTPWCD938 [SM8250-MTP-WCD9380-WSA8810-VA-D], device 1: MultiMedia2 (*) []
|
||
|
Subdevices: 1/1
|
||
|
Subdevice #0: subdevice #0
|
||
|
card 1: Seri [Plantronics Blackwire 3225 Seri], device 0: USB Audio [USB Audio]
|
||
|
Subdevices: 1/1
|
||
|
Subdevice #0: subdevice #0
|
||
|
card 2: C320M [Plantronics C320-M], device 0: USB Audio [USB Audio]
|
||
|
Subdevices: 1/1
|
||
|
Subdevice #0: subdevice #0
|
||
|
|
||
|
**USB Sound Card** - card#1:
|
||
|
|
||
|
::
|
||
|
|
||
|
USB Offload Playback Card Route PCM#0 -1 (range -1->32)
|
||
|
USB Offload Playback PCM Route PCM#0 -1 (range -1->255)
|
||
|
|
||
|
**USB Sound Card** - card#2:
|
||
|
|
||
|
::
|
||
|
|
||
|
USB Offload Playback Card Route PCM#0 0 (range -1->32)
|
||
|
USB Offload Playback PCM Route PCM#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 kcontrols lists
|
||
|
the mapped offload card and pcm device indexes for the specific USB device:
|
||
|
|
||
|
``USB Offload Playback Card Route PCM#*``
|
||
|
|
||
|
``USB Offload Playback PCM Route PCM#*``
|
||
|
|
||
|
The kcontrol is indexed, because a USB audio device could potentially have
|
||
|
several PCM devices. The above kcontrols are defined as:
|
||
|
|
||
|
- ``USB Offload Playback Card Route PCM#`` **(R)**: Returns the ASoC platform sound
|
||
|
card index for a mapped offload path. The output **"0"** (card index) signifies
|
||
|
that there is an available offload path for the USB SND device through card#0.
|
||
|
If **"-1"** is seen, then no offload path is available for the USB SND device.
|
||
|
This kcontrol exists for each USB audio device that exists in the system, and
|
||
|
its expected to derive the current status of offload based on the output value
|
||
|
for the kcontrol along with the PCM route kcontrol.
|
||
|
|
||
|
- ``USB Offload Playback PCM Route PCM#`` **(R)**: Returns the ASoC platform sound
|
||
|
PCM device index for a mapped offload path. The output **"1"** (PCM device index)
|
||
|
signifies that there is an available offload path for the USB SND device through
|
||
|
PCM device#0. If **"-1"** is seen, then no offload path is available for the USB\
|
||
|
SND device. This kcontrol exists for each USB audio device that exists in the
|
||
|
system, and its expected to derive the current status of offload based on the
|
||
|
output value for this kcontrol, in addition to the card route kcontrol.
|
||
|
|
||
|
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, int direction,
|
||
|
enum snd_soc_usb_kctl path,
|
||
|
long *route)
|
||
|
..
|
||
|
|
||
|
These are specific for the **USB Offload Playback Card Route PCM#** and **USB
|
||
|
Offload PCM Route PCM#** kcontrols.
|
||
|
|
||
|
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
|
||
|
will be added to the pre existing list of kcontrols identified by the USB sound
|
||
|
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 1 USB Offload Playback Card Route PCM#0 0 (range -1->32)
|
||
|
9 INT 1 USB Offload Playback PCM Route PCM#0 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.
|