From patchwork Tue May 7 19:51:06 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wesley Cheng X-Patchwork-Id: 13657669 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 smtp.subspace.kernel.org (Postfix) with ESMTPS id BDF8D181312; Tue, 7 May 2024 19:51:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715111519; cv=none; b=EHJfCmMCK3WQYinV0xaiktqTFrD9M3roF72MD0lS9DuvWUysvZpLmaeXm9nYoVFWbnyFkx4KeDJoLedAHU/+45N7rs0e4eJcNhvm39vUo6wdLVJjWRn/zRAakLIbo8+FYnr/Tj5uYn8h7/tJ6kiL7mJVCG/lIocQFHUPpHpwbgA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715111519; c=relaxed/simple; bh=+nfcvGWZD5ZcYU6A5fxEGIAmbCkrm7ZeRMjZJ+onH/k=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=UGumExWtalF1Gx5caUbOpybaH/txeQuUUYd2B7GQrWL96GcCW/CCGYlVWownpLySjLJEol3i423G/41q4NezhTDNyme3rh2OTeOb77oKkFTA8YZIRmDGiZ3AiRFkSticj2SKu9cWHYO4rRqjo/LhZGrh1tl7LhX8shirJWG5wdE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com; spf=pass smtp.mailfrom=quicinc.com; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b=G2xHougM; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=quicinc.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b="G2xHougM" Received: from pps.filterd (m0279873.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 447JEtfu021335; Tue, 7 May 2024 19:51:37 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=5REuHV9F09W31LM094m6 eolUPHP+mFzdCy/jX322/oo=; b=G2xHougMS2byaWCdQaT60uky4GwPI95DQOeM mZBzUzhjhorGACNs24hg/QSWZUMxZRtLdwv3rHRF6EC4IYwDtq9VUFTcnrN6qNHE HMsxxJQAotDxg0PlKFxsSHJOujZF/BlpdDhMNCwGNZkQPnrOrBro4p1iwJ12HTCr A+7efxm/OqlnemZidMx3ebRL6VcLdtIFEcVFBt6A4TDusNq3zr/EmmvRNkM1eBLM O7jtppMZj2LtFn8h9y+W0YOwmKV5EhjHeTAVl2IwQTcjG3nP8FxFP0zRfpsaBWzC fxwHpL0zsNVpkDRH0qj5IhmAlw4NUiZ0LKszfw989rwt+gDFXQ== Received: from nalasppmta02.qualcomm.com (Global_NAT1.qualcomm.com [129.46.96.20]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 3xysph04ma-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 07 May 2024 19:51:36 +0000 (GMT) Received: from nalasex01b.na.qualcomm.com (nalasex01b.na.qualcomm.com [10.47.209.197]) by NALASPPMTA02.qualcomm.com (8.17.1.5/8.17.1.5) with ESMTPS id 447JpZKN030857 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 7 May 2024 19:51:35 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.1544.9; Tue, 7 May 2024 12:51:35 -0700 From: Wesley Cheng To: , , , , , , , , , , , , , , , CC: , , , , , , , Wesley Cheng Subject: [PATCH v21 29/39] ASoC: Introduce SND kcontrols to track USB offloading state Date: Tue, 7 May 2024 12:51:06 -0700 Message-ID: <20240507195116.9464-30-quic_wcheng@quicinc.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20240507195116.9464-1-quic_wcheng@quicinc.com> References: <20240507195116.9464-1-quic_wcheng@quicinc.com> Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-ClientProxiedBy: nalasex01a.na.qualcomm.com (10.47.209.196) 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: nfkFPdRNXykdr79MChOSg7_FS_L65Qn_ X-Proofpoint-GUID: nfkFPdRNXykdr79MChOSg7_FS_L65Qn_ X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1039,Hydra:6.0.650,FMLib:17.11.176.26 definitions=2024-05-07_12,2024-05-06_02,2023-05-22_02 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 mlxlogscore=999 impostorscore=0 priorityscore=1501 bulkscore=0 phishscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 malwarescore=0 adultscore=0 suspectscore=0 mlxscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2405010000 definitions=main-2405070138 Expose helpers in the SoC USB layer so components can update and keep track of the offloading sessions. This exposes a kcontrol to userspace, so that applications can be aware of what the current USB offloading status is. An example output using tinymix is: USB offloading idle: tinymix -D 0 get 'USB Offload Playback Route Status' -->-1, -1 (range -1->32) USB offloading active(USB card#1 pcm#0): tinymix -D 0 get 'USB Offload Playback Route Status' -->1, 0 (range -1->32) Signed-off-by: Wesley Cheng --- include/sound/soc-usb.h | 46 +++++++++++ sound/soc/soc-usb.c | 176 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 222 insertions(+) diff --git a/include/sound/soc-usb.h b/include/sound/soc-usb.h index 56f74dbb3600..c5918b805d6a 100644 --- a/include/sound/soc-usb.h +++ b/include/sound/soc-usb.h @@ -9,9 +9,29 @@ enum snd_soc_usb_kctl { SND_SOC_USB_KCTL_CARD_ROUTE, SND_SOC_USB_KCTL_PCM_ROUTE, + SND_SOC_USB_KCTL_CARD_STATUS, + SND_SOC_USB_KCTL_PCM_STATUS, SND_SOC_USB_KCTL_MAX, }; +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 @@ -31,6 +51,7 @@ struct snd_soc_usb_device { * @list - list head for SND SOC struct list * @component - reference to ASoC component * @kctl - list of kcontrols created + * @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 @@ -41,6 +62,7 @@ struct snd_soc_usb { struct list_head list; struct snd_soc_component *component; struct snd_kcontrol *kctl[SND_SOC_USB_KCTL_MAX]; + 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); @@ -62,6 +84,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_streams, void *data); void snd_soc_usb_free_port(struct snd_soc_usb *usb); @@ -97,6 +124,25 @@ static inline void *snd_soc_usb_find_priv_data(struct device *dev) return NULL; } +static inline int snd_soc_usb_prepare_session(struct snd_soc_usb *usb, int card_idx, + int pcm_idx) +{ + return -EINVAL; +} + +static inline int snd_soc_usb_shutdown_session(struct snd_soc_usb *usb, + int session_id) +{ + return -EINVAL; +} + +static inline int snd_soc_usb_set_session_state(struct snd_soc_usb *usb, + int session_id, + enum snd_soc_usb_dai_state state) +{ + return -EINVAL; +} + static inline struct snd_soc_usb *snd_soc_usb_allocate_port( struct snd_soc_component *component, int num_streams, void *data) diff --git a/sound/soc/soc-usb.c b/sound/soc/soc-usb.c index 6f8fd9acd5dd..f699ed89961a 100644 --- a/sound/soc/soc-usb.c +++ b/sound/soc/soc-usb.c @@ -61,6 +61,79 @@ static struct snd_soc_usb *snd_soc_find_usb_ctx(struct device *dev) } /* SOC USB sound kcontrols */ +static int snd_soc_usb_get_offload_card_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_usb_ctx_lookup(component->dev->of_node); + int control_idx = 0; + int card_idx; + int i; + + for (i = 0; i < ctx->num_supported_streams; i++) { + card_idx = -1; + + if (ctx->active_list[i].state == SND_SOC_USB_RUNNING) + card_idx = ctx->active_list[i].active_card_idx; + + ucontrol->value.integer.value[control_idx] = card_idx; + control_idx++; + } + + return 0; +} + +static int snd_soc_usb_offload_card_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_usb_ctx_lookup(component->dev->of_node); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = ctx->num_supported_streams; + uinfo->value.integer.min = -1; + uinfo->value.integer.max = SNDRV_CARDS; + + return 0; +} + +static int snd_soc_usb_get_offload_pcm_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_usb_ctx_lookup(component->dev->of_node); + int control_idx = 0; + int pcm_idx; + int i; + + for (i = 0; i < ctx->num_supported_streams; i++) { + pcm_idx = -1; + + if (ctx->active_list[i].state == SND_SOC_USB_RUNNING) + pcm_idx = ctx->active_list[i].active_pcm_idx; + + ucontrol->value.integer.value[control_idx] = pcm_idx; + control_idx++; + } + + return 0; +} + +static int snd_soc_usb_offload_pcm_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_usb_ctx_lookup(component->dev->of_node); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = ctx->num_supported_streams; + uinfo->value.integer.min = -1; + /* Arbitrary max value, as there is no 'limit' on number of PCM devices */ + uinfo->value.integer.max = 0xff; + + return 0; +} + static int soc_usb_put_offload_pcm_dev(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -171,6 +244,22 @@ static const struct snd_kcontrol_new soc_usb_kcontrols[] = { .get = soc_usb_get_offload_pcm_dev, .put = soc_usb_put_offload_pcm_dev, }, + [SND_SOC_USB_KCTL_CARD_STATUS] = { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .name = "USB Offload Playback Route Card Status", + .info = snd_soc_usb_offload_card_status_info, + .get = snd_soc_usb_get_offload_card_status, + .put = NULL, + }, + [SND_SOC_USB_KCTL_PCM_STATUS] = { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .name = "USB Offload Playback Route PCM Status", + .info = snd_soc_usb_offload_pcm_status_info, + .get = snd_soc_usb_get_offload_pcm_status, + .put = NULL, + }, }; static int snd_soc_usb_control_remove(struct snd_soc_usb *usb) @@ -206,6 +295,85 @@ static int snd_soc_usb_control_init(struct snd_soc_usb *usb) return ret; } +/** + * 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 @@ -284,6 +452,13 @@ 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; @@ -302,6 +477,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);