[v15,39/50] ASoC: Introduce SND kcontrols to select sound card and PCM device

Message ID 20240213005422.3121-40-quic_wcheng@quicinc.com
State New
Headers
Series Introduce QC USB SND audio offloading support |

Commit Message

Wesley Cheng Feb. 13, 2024, 12:54 a.m. UTC
  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 'USB Offload Playback Route 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(-)
  

Comments

Takashi Iwai Feb. 13, 2024, 10:54 a.m. UTC | #1
On Tue, 13 Feb 2024 01:54:11 +0100,
Wesley Cheng wrote:
> 
> +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);

It might be safer to initialize the values with -1 in case when the
callback isn't available?

>  /**
>   * 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);

We may need to remove the control element upon the driver removal,
too?  In theory, you can unload the offload stuff while snd-usb-audio
is still active.


thanks,

Takashi
  
Wesley Cheng Feb. 14, 2024, 10:57 p.m. UTC | #2
Hi Takashi,

On 2/13/2024 2:54 AM, Takashi Iwai wrote:
> On Tue, 13 Feb 2024 01:54:11 +0100,
> Wesley Cheng wrote:
>>
>> +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);
> 
> It might be safer to initialize the values with -1 in case when the
> callback isn't available?
> 

Make sense, will do.

>>   /**
>>    * 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);
> 
> We may need to remove the control element upon the driver removal,
> too?  In theory, you can unload the offload stuff while snd-usb-audio
> is still active.
> 

This is managing the control for the ASoC platform card that supports 
offloading.  If we compile the different kernel entities as modules, we 
see that the soc_usb layer has the following users:

soc_usb 16384 3 q6usb,snd_usb_audio_qmi,snd_soc_qcom_offload_utils

So we'd need to remove all these modules before we can unload soc_usb. 
The entity which would be related to the kcontrols created for the ASoC 
sound card would be the q6usb module.  Since the q6usb module is tightly 
coupled with the platform soundcard itself in our design, for the q6usb 
module to be removed, we have to remove the entire ASoC sound card. (in 
which the kcontrols will also be removed)

However, that might not always be the case...so its a good idea to just 
manually remove the kcontrols in soc-usb as you guided.

Thanks
Wesley Cheng
  

Patch

diff --git a/include/sound/soc-usb.h b/include/sound/soc-usb.h
index 2790b51d979e..7b0531f975c2 100644
--- a/include/sound/soc-usb.h
+++ b/include/sound/soc-usb.h
@@ -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;
 };
 
diff --git a/sound/soc/soc-usb.c b/sound/soc/soc-usb.c
index e3556c397b39..a55d1c509297 100644
--- a/sound/soc/soc-usb.c
+++ b/sound/soc/soc-usb.c
@@ -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 = "USB Offload Playback Route 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);