Add SND kcontrol to SOC USB, which will allow for userpsace to determine
which USB card number and PCM device to offload. This allows for userspace
to potentially tag an alternate path for a specific USB SND card and PCM
device. Previously, control was absent, and the offload path would be
enabled on the last USB SND device which was connected. This logic will
continue to be applicable if no mixer input is received for specific device
selection.
An example to configure the offload device using tinymix:
tinymix -D 0 set 'SNDUSB OFFLD device select' 1 0
The above command will configure the offload path to utilize card#1 and PCM
stream#0.
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
---
include/sound/soc-usb.h | 7 ++++-
sound/soc/soc-usb.c | 67 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 73 insertions(+), 1 deletion(-)
On Fri, 09 Feb 2024 00:13:54 +0100,
Wesley Cheng wrote:
>
> Add SND kcontrol to SOC USB, which will allow for userpsace to determine
> which USB card number and PCM device to offload. This allows for userspace
> to potentially tag an alternate path for a specific USB SND card and PCM
> device. Previously, control was absent, and the offload path would be
> enabled on the last USB SND device which was connected. This logic will
> continue to be applicable if no mixer input is received for specific device
> selection.
>
> An example to configure the offload device using tinymix:
> tinymix -D 0 set 'SNDUSB OFFLD device select' 1 0
As I mentioned in another patch, the control element name should be
more understandable. The same applied even for ASoC stuff.
The current name is way too cryptic.
thanks,
Takashi
@@ -26,14 +26,19 @@ struct snd_soc_usb_device {
* @dev - USB backend device reference
* @component - reference to ASoC component
* @connection_status_cb - callback to notify connection events
+ * @put_offload_dev - callback to select USB sound card/PCM device
+ * @get_offload_dev - callback to fetch selected USB sound card/PCM device
* @priv_data - driver data
**/
struct snd_soc_usb {
struct list_head list;
- struct device *dev;
struct snd_soc_component *component;
int (*connection_status_cb)(struct snd_soc_usb *usb,
struct snd_soc_usb_device *sdev, bool connected);
+ int (*put_offload_dev)(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+ int (*get_offload_dev)(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
void *priv_data;
};
@@ -15,6 +15,9 @@ static struct device_node *snd_soc_find_phandle(struct device *dev)
{
struct device_node *node;
+ if (!dev)
+ return ERR_PTR(-ENODEV);
+
node = of_parse_phandle(dev->of_node, "usb-soc-be", 0);
if (!node)
return ERR_PTR(-ENODEV);
@@ -38,6 +41,64 @@ static struct snd_soc_usb *snd_soc_find_usb_ctx(struct device_node *node)
return NULL;
}
+/* SOC USB sound kcontrols */
+static int soc_usb_put_offload_dev(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_usb *ctx = snd_soc_usb_find_priv_data(component->dev);
+ int ret = 0;
+
+ mutex_lock(&ctx_mutex);
+ if (ctx && ctx->put_offload_dev)
+ ret = ctx->put_offload_dev(kcontrol, ucontrol);
+ mutex_unlock(&ctx_mutex);
+
+ return ret;
+}
+
+static int soc_usb_get_offload_dev(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_usb *ctx = snd_soc_usb_find_priv_data(component->dev);
+ int ret = 0;
+
+ mutex_lock(&ctx_mutex);
+ if (ctx && ctx->get_offload_dev)
+ ret = ctx->get_offload_dev(kcontrol, ucontrol);
+ mutex_unlock(&ctx_mutex);
+
+ return ret;
+
+}
+
+static int soc_usb_offload_dev_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = -1;
+ uinfo->value.integer.max = SNDRV_CARDS;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new soc_usb_dev_ctrl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .name = "SNDUSB OFFLD device select",
+ .info = soc_usb_offload_dev_info,
+ .get = soc_usb_get_offload_dev,
+ .put = soc_usb_put_offload_dev,
+};
+
+static int snd_soc_usb_control_init(struct snd_soc_component *component)
+{
+ return snd_ctl_add(component->card->snd_card,
+ snd_ctl_new1(&soc_usb_dev_ctrl, component));
+}
+
/**
* snd_soc_usb_get_components_tag() - Retrieve SOC USB component tag
* @playback: direction of audio stream
@@ -157,6 +218,12 @@ EXPORT_SYMBOL_GPL(snd_soc_usb_free_port);
*/
int snd_soc_usb_add_port(struct snd_soc_usb *usb)
{
+ int ret;
+
+ ret = snd_soc_usb_control_init(usb->component);
+ if (ret < 0)
+ return ret;
+
mutex_lock(&ctx_mutex);
list_add_tail(&usb->list, &usb_ctx_list);
mutex_unlock(&ctx_mutex);