From patchwork Sat Feb 3 02:36:35 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wesley Cheng X-Patchwork-Id: 769533 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 38304C4828F for ; Sat, 3 Feb 2024 02:47:25 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 46D291903; Sat, 3 Feb 2024 03:47:13 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 46D291903 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1706928443; bh=XHbx/v/TuXqw8WQvMgvyY6h+6CwDlT1EQepdFQ4hDLM=; h=From:To:CC:Subject:Date:In-Reply-To:References:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=sxFqfII+iDxSfs4+EjBg5tHuL7lIVm2iiWPYCeED5pp+ayXz5yOnWkpyoOoz0y+4Q KpBrIdNpjgmmutNoJVmoSRd3EI4rYLjE4ROS4pHyZJcQuZtne1LOwzVeNpiG5sX50i t6MlXN7OFH4aXsiOJ5h6iNTVBdd9y3gGzyCqqoxU= Received: by alsa1.perex.cz (Postfix, from userid 50401) id E9368F89850; Sat, 3 Feb 2024 03:39:56 +0100 (CET) Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id DAB51F8983B; Sat, 3 Feb 2024 03:39:55 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id CFF30F80CAA; Sat, 3 Feb 2024 03:39:16 +0100 (CET) Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id B61A4F805AD for ; Sat, 3 Feb 2024 03:37:33 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz B61A4F805AD Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key, unprotected) header.d=quicinc.com header.i=@quicinc.com header.a=rsa-sha256 header.s=qcppdkim1 header.b=J4OK3gk7 Received: from pps.filterd (m0279872.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.17.1.24/8.17.1.24) with ESMTP id 4132QjKs029159; Sat, 3 Feb 2024 02:37:32 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h= from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-type; s=qcppdkim1; bh=60ObKyG9TuHBxrZeqJMr xbRUfxtLp+1tF0A4Dzl6/6A=; b=J4OK3gk7DNZV8jI5zFkNIicxTbyVkt/Ge+B0 pRllEeKpawUokwnY2IRPevo29sgSC6c7+OnKdQ+FQgMQNmbICTHQMtrtdIDN4J2F r6wSr9ud1OxNoWqttOgo21NzJjiY3/hD+h9PmZv+z/g+rR+/ntpTxfJZqMcneAK6 wYgDJJ5goMw+v5Llrg7kevfRuPZswTpO3en0bpYFElbyThEeo8PNSCmoeAbnbmHJ vXA6y4wauA3BqW6Od/pz9l6jp0SyiECTXNY0LCfpiWS6DSXa56AziHLo6N2Z30E6 f7XkfduP8jieLNQ3y8Kn8zXtFkrA+FmH7Xb9Ba2lkIxUQnFh8g== Received: from nalasppmta03.qualcomm.com (Global_NAT1.qualcomm.com [129.46.96.20]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 3w1ax4849m-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Sat, 03 Feb 2024 02:37:31 +0000 (GMT) Received: from nalasex01b.na.qualcomm.com (nalasex01b.na.qualcomm.com [10.47.209.197]) by NALASPPMTA03.qualcomm.com (8.17.1.5/8.17.1.5) with ESMTPS id 4132bUCr012287 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Sat, 3 Feb 2024 02:37:30 GMT Received: from hu-wcheng-lv.qualcomm.com (10.49.16.6) by nalasex01b.na.qualcomm.com (10.47.209.197) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.40; Fri, 2 Feb 2024 18:37:30 -0800 From: Wesley Cheng To: , , , , , , , , , , , , , , CC: , , , , , , , Wesley Cheng Subject: [PATCH v13 43/53] ASoC: Add SND kcontrol for fetching USB offload status Date: Fri, 2 Feb 2024 18:36:35 -0800 Message-ID: <20240203023645.31105-44-quic_wcheng@quicinc.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20240203023645.31105-1-quic_wcheng@quicinc.com> References: <20240203023645.31105-1-quic_wcheng@quicinc.com> MIME-Version: 1.0 X-Originating-IP: [10.49.16.6] X-ClientProxiedBy: nalasex01b.na.qualcomm.com (10.47.209.197) To nalasex01b.na.qualcomm.com (10.47.209.197) X-QCInternal: smtphost X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=5800 signatures=585085 X-Proofpoint-ORIG-GUID: a9njwXRwMqosq7zR3H2dLEK_LdAJw9_2 X-Proofpoint-GUID: a9njwXRwMqosq7zR3H2dLEK_LdAJw9_2 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.272,Aquarius:18.0.1011,Hydra:6.0.619,FMLib:17.11.176.26 definitions=2024-02-02_16,2024-01-31_01,2023-05-22_02 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 lowpriorityscore=0 clxscore=1015 bulkscore=0 phishscore=0 mlxlogscore=999 priorityscore=1501 impostorscore=0 malwarescore=0 spamscore=0 adultscore=0 suspectscore=0 mlxscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2401310000 definitions=main-2402030015 Message-ID-Hash: EA5X3LDOYIJ3WURHWWKVOWQU2QLXOW5V X-Message-ID-Hash: EA5X3LDOYIJ3WURHWWKVOWQU2QLXOW5V X-MailFrom: quic_wcheng@quicinc.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.9 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Add a kcontrol to the platform sound card to fetch the current offload status. This can allow for userspace to ensure/check which USB SND resources are actually busy versus having to attempt opening the USB SND devices, which will result in an error if offloading is active. Signed-off-by: Wesley Cheng --- include/sound/soc-usb.h | 27 ++++++++ sound/soc/soc-usb.c | 150 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 175 insertions(+), 2 deletions(-) diff --git a/include/sound/soc-usb.h b/include/sound/soc-usb.h index 20d7b32bba07..c05d9b2f5c90 100644 --- a/include/sound/soc-usb.h +++ b/include/sound/soc-usb.h @@ -6,6 +6,24 @@ #ifndef __LINUX_SND_SOC_USB_H #define __LINUX_SND_SOC_USB_H +enum snd_soc_usb_dai_state { + SND_SOC_USB_IDLE, + SND_SOC_USB_PREPARED, + SND_SOC_USB_RUNNING, +}; + +/** + * struct snd_soc_usb_session + * @active_card_idx - active offloaded sound card + * @active_pcm_idx - active offloaded PCM device + * @state - USB BE DAI link PCM state + */ +struct snd_soc_usb_session { + int active_card_idx; + int active_pcm_idx; + enum snd_soc_usb_dai_state state; +}; + /** * struct snd_soc_usb_device * @card_idx - sound card index associated with USB device @@ -25,6 +43,8 @@ struct snd_soc_usb_device { * @list - list head for SND SOC struct list * @dev - USB backend device reference * @component - reference to ASoC component + * @active_list - active sessions + * @num_supported_streams - number of supported concurrent sessions * @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 @@ -33,6 +53,8 @@ struct snd_soc_usb_device { struct snd_soc_usb { struct list_head list; struct snd_soc_component *component; + struct snd_soc_usb_session *active_list; + unsigned int num_supported_streams; 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, @@ -50,6 +72,11 @@ int snd_soc_usb_connect(struct device *usbdev, struct snd_soc_usb_device *sdev); int snd_soc_usb_disconnect(struct device *usbdev, struct snd_soc_usb_device *sdev); void *snd_soc_usb_find_priv_data(struct device *dev); +int snd_soc_usb_prepare_session(struct snd_soc_usb *usb, int card_idx, int pcm_idx); +int snd_soc_usb_shutdown_session(struct snd_soc_usb *usb, int session_id); +int snd_soc_usb_set_session_state(struct snd_soc_usb *usb, int session_id, + enum snd_soc_usb_dai_state state); + struct snd_soc_usb *snd_soc_usb_allocate_port(struct snd_soc_component *component, int num_supported_streams, void *data); void snd_soc_usb_free_port(struct snd_soc_usb *usb); diff --git a/sound/soc/soc-usb.c b/sound/soc/soc-usb.c index c568c67e3e4a..9c082129cb9f 100644 --- a/sound/soc/soc-usb.c +++ b/sound/soc/soc-usb.c @@ -42,11 +42,62 @@ static struct snd_soc_usb *snd_soc_find_usb_ctx(struct device_node *node) } /* SOC USB sound kcontrols */ +static int snd_soc_usb_get_offload_status(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_find_usb_ctx(component->dev->of_node); + int control_idx = 0; + int pcm_idx; + int card_idx; + int i; + + for (i = 0; i < ctx->num_supported_streams; i++) { + card_idx = -1; + pcm_idx = -1; + + if (ctx->active_list[i].state == SND_SOC_USB_RUNNING) { + card_idx = ctx->active_list[i].active_card_idx; + pcm_idx = ctx->active_list[i].active_pcm_idx; + } + + ucontrol->value.integer.value[control_idx] = card_idx; + control_idx++; + ucontrol->value.integer.value[control_idx] = pcm_idx; + control_idx++; + } + + return 0; +} + +static int snd_soc_usb_offload_status_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct snd_soc_usb *ctx = snd_soc_find_usb_ctx(component->dev->of_node); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2*ctx->num_supported_streams; + uinfo->value.integer.min = -1; + uinfo->value.integer.max = SNDRV_CARDS; + + return 0; +} + +static const struct snd_kcontrol_new soc_usb_status_ctrl = { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .name = "SNDUSB OFFLD playback status", + .info = snd_soc_usb_offload_status_info, + .get = snd_soc_usb_get_offload_status, + .put = NULL, +}; + 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); + struct snd_soc_usb *ctx = snd_soc_find_usb_ctx(component->dev->of_node); int ret = 0; mutex_lock(&ctx_mutex); @@ -61,7 +112,7 @@ 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); + struct snd_soc_usb *ctx = snd_soc_find_usb_ctx(component->dev->of_node); int ret = 0; mutex_lock(&ctx_mutex); @@ -95,10 +146,96 @@ static const struct snd_kcontrol_new soc_usb_dev_ctrl = { static int snd_soc_usb_control_init(struct snd_soc_component *component) { + int ret; + + ret = snd_ctl_add(component->card->snd_card, + snd_ctl_new1(&soc_usb_status_ctrl, component)); + if (ret < 0) + return ret; + return snd_ctl_add(component->card->snd_card, snd_ctl_new1(&soc_usb_dev_ctrl, component)); } +/** + * snd_soc_usb_set_session_state() - Set the session state for a session + * @usb: SOC USB device + * @session_id: index to active_list + * @state: USB PCM device index + * + * Set the session state for an entry in active_list. This should be only + * called after snd_soc_usb_prepare_session. + * + * Returns 0 on success, negative on error. + * + */ +int snd_soc_usb_set_session_state(struct snd_soc_usb *usb, int session_id, + enum snd_soc_usb_dai_state state) +{ + if (session_id < 0 || session_id >= usb->num_supported_streams) + return -EINVAL; + + mutex_lock(&ctx_mutex); + if (usb->active_list[session_id].state == state) { + mutex_unlock(&ctx_mutex); + return 0; + } + + usb->active_list[session_id].state = state; + mutex_unlock(&ctx_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_usb_set_session_state); + +/** + * snd_soc_usb_prepare_session() - Find and prepare a session + * @usb: SOC USB device + * @card_idx: USB card index + * @pcm_idx: USB PCM device index + * + * Find an open active session slot on the SOC USB device. If all slots + * are busy, return an error. If not, claim the slot and place it into + * the SND_SOC_USB_PREPARED state. This should be called first before + * calling snd_soc_usb_set_session_state or snd_soc_usb_shutdown_session. + * + * Returns the session id (index) to active_list, negative on error. + * + */ +int snd_soc_usb_prepare_session(struct snd_soc_usb *usb, int card_idx, int pcm_idx) +{ + int i; + + mutex_lock(&ctx_mutex); + for (i = 0; i < usb->num_supported_streams; i++) { + if (usb->active_list[i].state == SND_SOC_USB_IDLE) { + usb->active_list[i].active_card_idx = card_idx; + usb->active_list[i].active_pcm_idx = pcm_idx; + usb->active_list[i].state = SND_SOC_USB_PREPARED; + mutex_unlock(&ctx_mutex); + return i; + } + } + mutex_unlock(&ctx_mutex); + + return -EBUSY; +} +EXPORT_SYMBOL_GPL(snd_soc_usb_prepare_session); + +/** + * snd_soc_usb_shutdown_session() - Set USB SOC to idle state + * @usb: SOC USB device + * @session_id: index to active_list + * + * Place the session specified by session_id into the idle/shutdown state. + * + */ +int snd_soc_usb_shutdown_session(struct snd_soc_usb *usb, int session_id) +{ + return snd_soc_usb_set_session_state(usb, session_id, SND_SOC_USB_IDLE); +} +EXPORT_SYMBOL_GPL(snd_soc_usb_shutdown_session); + /** * snd_soc_usb_get_components_tag() - Retrieve SOC USB component tag * @playback: direction of audio stream @@ -185,8 +322,16 @@ struct snd_soc_usb *snd_soc_usb_allocate_port(struct snd_soc_component *componen if (!usb) return ERR_PTR(-ENOMEM); + usb->active_list = kcalloc(num_streams, sizeof(struct snd_soc_usb_session), + GFP_KERNEL); + if (!usb->active_list) { + kfree(usb); + return ERR_PTR(-ENOMEM); + } + usb->component = component; usb->priv_data = data; + usb->num_supported_streams = num_streams; return usb; } @@ -202,6 +347,7 @@ EXPORT_SYMBOL_GPL(snd_soc_usb_allocate_port); void snd_soc_usb_free_port(struct snd_soc_usb *usb) { snd_soc_usb_remove_port(usb); + kfree(usb->active_list); kfree(usb); } EXPORT_SYMBOL_GPL(snd_soc_usb_free_port);