From nobody Wed Dec 17 07:35:00 2025 Received: from mx0b-001ae601.pphosted.com (mx0b-001ae601.pphosted.com [67.231.152.168]) (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 E975A1E98EC; Thu, 27 Mar 2025 13:00:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=67.231.152.168 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1743080443; cv=fail; b=MmwXFcHyhx019bnpQmf34peZmxjbeungrzhuMJCJ6umMRV+zZQBgK4cHdDX1AvSinsmSK1itnrK9cJwr9VSnOYluRfvWUDMmWoh8ehYRQdvCjDkh0N+flYCG/z3D/psZqM7vdapSzqAxSkY998v6p9s63xe+i3bLim0ag6jIyLg= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1743080443; c=relaxed/simple; bh=/W9rLoRk6TVnW30rOVz1fP6pr7KFpKx3z9pe96z8pcM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=dQrIKqQdF0PHjEWcdKRPhL1PzxxJwaAdO7n1FTqpVRKL/19u86JrYji4v2ENACyA7fFlLfdQ6CFMOB75UNV/KpMEInAiG+HNMClN4bcMXF5sPxzHihATFPSn3Sk9Ug1QSdWH92tgzHhbyNposBY/fAoTLB0L/UOJlK2XvtsxtKY= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=opensource.cirrus.com; spf=pass smtp.mailfrom=cirrus.com; dkim=pass (2048-bit key) header.d=cirrus.com header.i=@cirrus.com header.b=V+XWEnpi; dkim=pass (1024-bit key) header.d=cirrus4.onmicrosoft.com header.i=@cirrus4.onmicrosoft.com header.b=m8ryUgWb; arc=fail smtp.client-ip=67.231.152.168 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=opensource.cirrus.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=cirrus.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=cirrus.com header.i=@cirrus.com header.b="V+XWEnpi"; dkim=pass (1024-bit key) header.d=cirrus4.onmicrosoft.com header.i=@cirrus4.onmicrosoft.com header.b="m8ryUgWb" Received: from pps.filterd (m0077474.ppops.net [127.0.0.1]) by mx0b-001ae601.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 52R5S2wl001946; Thu, 27 Mar 2025 08:00:25 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cirrus.com; h=cc :content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s= PODMain02222019; bh=90aRe2ztHCgymW+YJY+/kfi+CnTTrGL+xgC2POaWxWU=; b= V+XWEnpioFcP5MYTDPeraUaiBToUw3LWRaC1/ma5/HCbxIS6KmADxe5bEH7PvBFZ nYNA+On7lZcpDMFs0aNlt+rRvVeIXi93gyNRLblgPct+IIE+4wjetYfuoyqg0JEz wGp7Wi2MZKYEMiBchGoNB2pha1qe5QUm2PiRquh+9RHFcHhmxTcuyS/J0K6xiJ9Q 3aWwB7HBGFVyZmvmTskJG2ouedskwd5lAuF6hPMIIiweRlbJ+JxssBA/i5vKiytn Sl0IFv7zBLAToo6DWnCveU3BLCvMHOmVJcbGRKwln/7f9qbqW5CmYbm2O4vJcQTo mA3WUl1xRDSTz53JV8MvNw== Received: from nam11-dm6-obe.outbound.protection.outlook.com (mail-dm6nam11lp2171.outbound.protection.outlook.com [104.47.57.171]) by mx0b-001ae601.pphosted.com (PPS) with ESMTPS id 45jntbyeew-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Thu, 27 Mar 2025 08:00:24 -0500 (CDT) ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=cJXu9deQUP79JO1LnTCan7X/Gd3ZqF2uRzFujNUkHeZ967IBm8bW7Ab63fgmlqfq3FSBvCTBA+QFArQPL9WtXI5xyqMjkIl/nShypKo9plony6EP0/CWjE7H8+ddRQjbg6YHpFHz0bIN8L7ydOfkrzLT+Ih1a5DEper4x3RVvVMbWut500cHvh7OmSRY6z5H2wmhk2XYn88qF1xCFGQsLSol+XfA+zjtNHVgc3CKAimxIjLGCgnt8xWM//+vHjjusdeXJcjPFr8Dskhtcw62iyuU7mMbruNUxx2+sHhcHHyMmK2FYwT3UtkPdmI+TfS3FRnZaemk62N7tk0HE38t0g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=90aRe2ztHCgymW+YJY+/kfi+CnTTrGL+xgC2POaWxWU=; b=bxLSKR5E/isN8ZWpzse6ouzHBF/ahoxY4U+C4l0ss6aObm6cWI/Iu4j2GTWiz7lxiUWNldBj5IWOhXDw3CywgaUWrOg4eZGmlCIUtimG7HP5H5jBXUHDpj7V8w3kNOVm/zQH3nXfMv5IcIk+eg+ddOKQxYeUwWsPORFk49llISBdSrTqj5RONhoYh/OPmC/f6Bf+6dOWb0JfvUBl+OkrjrnlGB+qJV+sr4SdmiPBw7TH7qA9wGgNgMR/xJPNKuejXaPnJnbOpxGYwf/J9lJQ0rmhOqEqE2z4JQzfQJvZzY+54UZOVaLAY8OmAtNJ67VKHx+QtCxFhcOMoorCqbwJ8A== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=fail (sender ip is 84.19.233.75) smtp.rcpttodomain=cirrus.com smtp.mailfrom=cirrus.com; dmarc=fail (p=reject sp=reject pct=100) action=oreject header.from=opensource.cirrus.com; dkim=none (message not signed); arc=none (0) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cirrus4.onmicrosoft.com; s=selector2-cirrus4-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=90aRe2ztHCgymW+YJY+/kfi+CnTTrGL+xgC2POaWxWU=; b=m8ryUgWb6V6nWGQlIaoYqrV/h28Lrmokmao9Hhob28mnlg6+r67KxDJOOhsWH4mlcPWIMfJXy/h4JsW5XY8KL4Drsy3OKV78tyhdkPmf8KJOpUntaz7MJ9QdsQSe/w7Hkn1jcDpCQDMWFqQHXnVHSDyNkui8KU/KVyb/DcPVGZM= Received: from DM6PR14CA0058.namprd14.prod.outlook.com (2603:10b6:5:18f::35) by CH3PR19MB7185.namprd19.prod.outlook.com (2603:10b6:610:153::5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8534.44; Thu, 27 Mar 2025 13:00:15 +0000 Received: from DS1PEPF00017090.namprd03.prod.outlook.com (2603:10b6:5:18f:cafe::b5) by DM6PR14CA0058.outlook.office365.com (2603:10b6:5:18f::35) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.20.8534.45 via Frontend Transport; Thu, 27 Mar 2025 13:00:14 +0000 X-MS-Exchange-Authentication-Results: spf=fail (sender IP is 84.19.233.75) smtp.mailfrom=cirrus.com; dkim=none (message not signed) header.d=none;dmarc=fail action=oreject header.from=opensource.cirrus.com; Received-SPF: Fail (protection.outlook.com: domain of cirrus.com does not designate 84.19.233.75 as permitted sender) receiver=protection.outlook.com; client-ip=84.19.233.75; helo=edirelay1.ad.cirrus.com; Received: from edirelay1.ad.cirrus.com (84.19.233.75) by DS1PEPF00017090.mail.protection.outlook.com (10.167.17.132) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.20.8534.20 via Frontend Transport; Thu, 27 Mar 2025 13:00:14 +0000 Received: from ediswmail9.ad.cirrus.com (ediswmail9.ad.cirrus.com [198.61.86.93]) by edirelay1.ad.cirrus.com (Postfix) with ESMTPS id ED43E406542; Thu, 27 Mar 2025 13:00:12 +0000 (UTC) Received: from ediswws07.ad.cirrus.com (ediswws07.ad.cirrus.com [198.90.208.14]) by ediswmail9.ad.cirrus.com (Postfix) with ESMTP id C607F82255C; Thu, 27 Mar 2025 13:00:12 +0000 (UTC) From: Charles Keepax To: broonie@kernel.org Cc: lgirdwood@gmail.com, pierre-louis.bossart@linux.dev, yung-chuan.liao@linux.intel.com, peter.ujfalusi@linux.intel.com, linux-sound@vger.kernel.org, linux-kernel@vger.kernel.org, patches@opensource.cirrus.com Subject: [PATCH v2 1/3] ASoC: SDCA: Create DAPM widgets and routes from DisCo Date: Thu, 27 Mar 2025 13:00:10 +0000 Message-Id: <20250327130012.3378732-2-ckeepax@opensource.cirrus.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250327130012.3378732-1-ckeepax@opensource.cirrus.com> References: <20250327130012.3378732-1-ckeepax@opensource.cirrus.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: DS1PEPF00017090:EE_|CH3PR19MB7185:EE_ X-MS-Office365-Filtering-Correlation-Id: 4cea2df5-5d1a-4e5a-6578-08dd6d2f50d9 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|376014|61400799027|36860700013|82310400026|13003099007; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?HtWdCbZ3mZybGFFU70wLa36XkzvMgKBluOxJvEt5iz/902wzhFId3ufrnBjP?= =?us-ascii?Q?1vB0zITigpBW+bGp64/8EJC51R7ueMH6sUabA6M7c1xI6/RsMgeigrgrbow2?= =?us-ascii?Q?PgP53Z4vixd0CYlnN9xw8+q9yms24msVhvp+FkvrZDDmWUweZFo5UhK5KVPG?= =?us-ascii?Q?G/Cikzbq2OvvoLCQcoBpyqgnTFCV2SYNdaC+MVll29o5emoSHp8RBfnEHusP?= =?us-ascii?Q?YmgXCnJqRyP0wrcZkhXjaMHuYkmkRBD3xGF/ZvGnIzkqv3z1KAvz5aGVlS5m?= =?us-ascii?Q?P6bJeB5afjbHrmIw3jDmpnXpo4Cs3FfYKunoqcuDA+IYvUSNXmk5TkXdLmSg?= =?us-ascii?Q?qJHWylHgn0ehxIv8bbtFD2meHJmIeRpquYbYZbMzBNoEtqRkQcHq/VXR6xGq?= =?us-ascii?Q?JQRci6ze/DYE1lpsyxAVpql85smOW87+AaosvXhjRf5/KWLzSTUTA5Jj+10x?= =?us-ascii?Q?Cilp0GvsVOORReaXphtzOShUPaHzBHZNVCbI3qIQHn/cRG9KBfaWCigK6mgb?= =?us-ascii?Q?Xz+IfeyJHdapHwgIcJtkNhDp/nE32WI8heelROB4z5QLAJYTrekFOJ9Bl9qR?= =?us-ascii?Q?49nRcbRDGXYB60rhWGPtQZa8vKPw+prFH+TTJNm0PH5bAKZOZxQJ6NO5I+OB?= =?us-ascii?Q?8+rc5g+5C9hsyJrZbt/Rn3soInNVvCkbNTd3e8vPKIJ6gDhuWLxdcz+xwQk3?= =?us-ascii?Q?4+5BX6feZ3Q9wp1TH5qIUatWfbHUI/HSiG8+3g/cMy2aZpv3Vk67CG8iiRPi?= =?us-ascii?Q?qBoFyzi7oH4TZivlYhlq3oofPUoK7w8+J5tup+uEZfij/+7o4tUQnRO/t2Dn?= =?us-ascii?Q?A6KFCZv56G26nQL48aVmg/O58eEQYTO+CW7wozo95VwekRD47fehMTjcyh3s?= =?us-ascii?Q?1gfcXzBePJXJMIpFjf0ywJRTbnLnSFQhYM2cpqRm60CYouZFg1wW2YHtl3YZ?= =?us-ascii?Q?4f5gmZSmRPoOghbQfbbW0Sv2SAMDAmjxSIuu3O3NTP1tL+tTb9F7b8f3O+KN?= =?us-ascii?Q?epK2W4iaVRPaYMmWD+daiI699IPq91loNV+sHmHSrnWGv3Vu81W5H9D/O8jh?= =?us-ascii?Q?CPr0wnIY6udRr7VACrnAmrIY9G7aMyjAG5cUsyq7l6SdgUKbKJRP1oh7uVze?= =?us-ascii?Q?QTN0bTCU06Pe5A8Zkgdo/aSK/2JpRtPIBlvhHBfaRoZl6lfaWlr3ZAbQtK3y?= =?us-ascii?Q?562SUKUgJ8n109odvK571tF1tGgX9m+TkIzpxU20JmFHOaeY7AKMxSC53Ujn?= =?us-ascii?Q?wB15r4aVDtsDOyQEpECtDH2A2CZ9NqTgvMXlvZs3TNn/HKdAY5K9JJJy7Lm9?= =?us-ascii?Q?eNgvSYVrJd/u/vx28fxO1ohdIQw6+a7+/1S+8wfRwObQUbzz2E71YjTSdavA?= =?us-ascii?Q?+RDw77JLi7fJ8OBxik00uZtsS3q2Jsc5JB0VP3jQ54bZtSWPlGcYy9mq49bd?= =?us-ascii?Q?TTBxTFYJSSokuSjjct+COCDnp2E67Ig1djKPSdIR1OFTnEgEpJ/5Vw=3D=3D?= X-Forefront-Antispam-Report: CIP:84.19.233.75;CTRY:GB;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:edirelay1.ad.cirrus.com;PTR:InfoDomainNonexistent;CAT:NONE;SFS:(13230040)(376014)(61400799027)(36860700013)(82310400026)(13003099007);DIR:OUT;SFP:1102; X-OriginatorOrg: opensource.cirrus.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 27 Mar 2025 13:00:14.0981 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 4cea2df5-5d1a-4e5a-6578-08dd6d2f50d9 X-MS-Exchange-CrossTenant-Id: bec09025-e5bc-40d1-a355-8e955c307de8 X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=bec09025-e5bc-40d1-a355-8e955c307de8;Ip=[84.19.233.75];Helo=[edirelay1.ad.cirrus.com] X-MS-Exchange-CrossTenant-AuthSource: DS1PEPF00017090.namprd03.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: CH3PR19MB7185 X-Authority-Analysis: v=2.4 cv=JJM7s9Kb c=1 sm=1 tr=0 ts=67e54be8 cx=c_pps a=gIIqiywzzXYl0XjYY6oQCA==:117 a=h1hSm8JtM9GN1ddwPAif2w==:17 a=wKuvFiaSGQ0qltdbU6+NXLB8nM8=:19 a=Ol13hO9ccFRV9qXi2t6ftBPywas=:19 a=Vs1iUdzkB0EA:10 a=s63m1ICgrNkA:10 a=RWc_ulEos4gA:10 a=KJT-RnjOAAAA:8 a=w1d2syhTAAAA:8 a=JvY5j6h5ebVhVtM4EWEA:9 a=HE_01F9_QflCRFonrIQr:22 a=BGLuxUZjE2igh1l4FkT-:22 X-Proofpoint-ORIG-GUID: NTfg9XDgSHikr9nwgKic5UtHG_96Sg-j X-Proofpoint-GUID: NTfg9XDgSHikr9nwgKic5UtHG_96Sg-j X-Proofpoint-Spam-Reason: safe Content-Type: text/plain; charset="utf-8" Use the previously parsed DisCo information from ACPI to create DAPM widgets and routes representing a SDCA Function. For the most part SDCA maps well to the DAPM abstractions. The primary point of interest is the SDCA Power Domain Entities (PDEs), which actually control the power status of the device. Whilst these PDEs are the primary widgets the other parts of the SDCA graph are added to maintain a consistency with the hardware abstract, and allow routing to take effect. As for the PDEs themselves the code currently only handle PS0 and PS3 (basically on and off), the two intermediate power states are not commonly used and don't map well to ASoC/DAPM. Other minor points of slightly complexity include, the Group Entities (GEs) these set the value of several other controls, typically Selector Units (SUs) for enabling a cetain jack configuration. Multiple SUs being controlled by a GE are easily modelled creating a single control and sharing it among the controlled muxes. SDCA also has a slight habit of having fully connected paths, relying more on activating the PDEs to enable functionality. This doesn't map quite so perfectly to DAPM which considers the path a reason to power the PDE. Whilst in the current specification Mixer Units are defined as fixed-function, in DAPM we create a virtual control for each input (which defaults to connected). This allows paths to be connected/disconnected, providing a more ASoC style approach to managing the power. In the future PIN_SWITCHs might be added as well to give more flexibility, but that is left as future work. A top level helper sdca_asoc_populate_component() is exported that counts and allocates everything, however, the intermediate counting and population functions are also exported. This will allow end drivers to do allocation and add custom handling, which is probably fairly likely for the early SDCA devices. Clock muxes are currently not fully supported, so some future work will also be required there. Signed-off-by: Charles Keepax --- Changes since v1: - Minor tweaks to commit messages - Fixed typo in "Microphone " string - Changed GE widget to a supply widget and linked it to the controlled SU widgets. This is nice than before when it was a kinda redundant unconnected widget and better matches the SDCA topology diagrams. include/sound/sdca_asoc.h | 30 ++ include/sound/sdca_function.h | 36 ++ sound/soc/sdca/Makefile | 2 +- sound/soc/sdca/sdca_asoc.c | 809 ++++++++++++++++++++++++++++++++++ 4 files changed, 876 insertions(+), 1 deletion(-) create mode 100644 include/sound/sdca_asoc.h create mode 100644 sound/soc/sdca/sdca_asoc.c diff --git a/include/sound/sdca_asoc.h b/include/sound/sdca_asoc.h new file mode 100644 index 000000000000..414d461b6fc4 --- /dev/null +++ b/include/sound/sdca_asoc.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * The MIPI SDCA specification is available for public downloads at + * https://www.mipi.org/mipi-sdca-v1-0-download + * + * Copyright (C) 2025 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef __SDCA_ASOC_H__ +#define __SDCA_ASOC_H__ + +struct device; +struct sdca_function_data; +struct snd_soc_component_driver; +struct snd_soc_dapm_route; +struct snd_soc_dapm_widget; + +int sdca_asoc_count_component(struct device *dev, struct sdca_function_dat= a *function, + int *num_widgets, int *num_routes); + +int sdca_asoc_populate_dapm(struct device *dev, struct sdca_function_data = *function, + struct snd_soc_dapm_widget *widgets, + struct snd_soc_dapm_route *routes); + +int sdca_asoc_populate_component(struct device *dev, + struct sdca_function_data *function, + struct snd_soc_component_driver *component_drv); + +#endif // __SDCA_ASOC_H__ diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h index 253654568a41..83fedc39cf71 100644 --- a/include/sound/sdca_function.h +++ b/include/sound/sdca_function.h @@ -257,6 +257,14 @@ enum sdca_pde_controls { SDCA_CTL_PDE_ACTUAL_PS =3D 0x10, }; =20 +/** + * enum sdca_requested_ps_range - Column definitions for Requested PS + */ +enum sdca_requested_ps_range { + SDCA_REQUESTED_PS_STATE =3D 0, + SDCA_REQUESTED_PS_NCOLS =3D 1, +}; + /** * enum sdca_ge_controls - SDCA Controls for Group Unit * @@ -268,6 +276,15 @@ enum sdca_ge_controls { SDCA_CTL_GE_DETECTED_MODE =3D 0x02, }; =20 +/** + * enum sdca_selected_mode_range - Column definitions for Selected Mode + */ +enum sdca_selected_mode_range { + SDCA_SELECTED_MODE_INDEX =3D 0, + SDCA_SELECTED_MODE_TERM_TYPE =3D 1, + SDCA_SELECTED_MODE_NCOLS =3D 2, +}; + /** * enum sdca_spe_controls - SDCA Controls for Security & Privacy Unit * @@ -773,6 +790,25 @@ enum sdca_terminal_type { SDCA_TERM_TYPE_PRIVACY_INDICATORS =3D 0x747, }; =20 +#define SDCA_TERM_TYPE_LINEIN_STEREO_NAME "LineIn Stereo" +#define SDCA_TERM_TYPE_LINEIN_FRONT_LR_NAME "LineIn Front-LR" +#define SDCA_TERM_TYPE_LINEIN_CENTER_LFE_NAME "LineIn Center-LFE" +#define SDCA_TERM_TYPE_LINEIN_SURROUND_LR_NAME "LineIn Surround-LR" +#define SDCA_TERM_TYPE_LINEIN_REAR_LR_NAME "LineIn Rear-LR" +#define SDCA_TERM_TYPE_LINEOUT_STEREO_NAME "LineOut Stereo" +#define SDCA_TERM_TYPE_LINEOUT_FRONT_LR_NAME "LineOut Front-LR" +#define SDCA_TERM_TYPE_LINEOUT_CENTER_LFE_NAME "LineOut Center-LFE" +#define SDCA_TERM_TYPE_LINEOUT_SURROUND_LR_NAME "LineOut Surround-LR" +#define SDCA_TERM_TYPE_LINEOUT_REAR_LR_NAME "LineOut Rear-LR" +#define SDCA_TERM_TYPE_MIC_JACK_NAME "Microphone" +#define SDCA_TERM_TYPE_STEREO_JACK_NAME "Speaker Stereo" +#define SDCA_TERM_TYPE_FRONT_LR_JACK_NAME "Speaker Front-LR" +#define SDCA_TERM_TYPE_CENTER_LFE_JACK_NAME "Speaker Center-LFE" +#define SDCA_TERM_TYPE_SURROUND_LR_JACK_NAME "Speaker Surround-LR" +#define SDCA_TERM_TYPE_REAR_LR_JACK_NAME "Speaker Rear-LR" +#define SDCA_TERM_TYPE_HEADPHONE_JACK_NAME "Headphone" +#define SDCA_TERM_TYPE_HEADSET_JACK_NAME "Headset" + /** * enum sdca_connector_type - SDCA Connector Types * diff --git a/sound/soc/sdca/Makefile b/sound/soc/sdca/Makefile index dddc3e694256..53344f108ca6 100644 --- a/sound/soc/sdca/Makefile +++ b/sound/soc/sdca/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only =20 -snd-soc-sdca-y :=3D sdca_functions.o sdca_device.o sdca_regmap.o +snd-soc-sdca-y :=3D sdca_functions.o sdca_device.o sdca_regmap.o sdca_asoc= .o =20 obj-$(CONFIG_SND_SOC_SDCA) +=3D snd-soc-sdca.o diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c new file mode 100644 index 000000000000..c91660f8dd4f --- /dev/null +++ b/sound/soc/sdca/sdca_asoc.c @@ -0,0 +1,809 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2025 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +/* + * The MIPI SDCA specification is available for public downloads at + * https://www.mipi.org/mipi-sdca-v1-0-download + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct sdca_control *selector_find_control(struct sdca_entity *enti= ty, + const int sel) +{ + int i; + + for (i =3D 0; i < entity->num_controls; i++) { + struct sdca_control *control =3D &entity->controls[i]; + + if (control->sel =3D=3D sel) + return control; + } + + return NULL; +} + +static struct sdca_control_range *control_find_range(struct device *dev, + struct sdca_entity *entity, + struct sdca_control *control, + int cols, int rows) +{ + struct sdca_control_range *range =3D &control->range; + + if ((cols && range->cols !=3D cols) || (rows && range->rows !=3D rows) || + !range->data) { + dev_err(dev, "%s: control %#x: ranges invalid (%d,%d)\n", + entity->label, control->sel, range->cols, range->rows); + return NULL; + } + + return range; +} + +static struct sdca_control_range *selector_find_range(struct device *dev, + struct sdca_entity *entity, + int sel, int cols, int rows) +{ + struct sdca_control *control; + + control =3D selector_find_control(entity, sel); + if (!control) { + dev_err(dev, "%s: control %#x: missing\n", entity->label, sel); + return NULL; + } + + return control_find_range(dev, entity, control, cols, rows); +} + +int sdca_asoc_count_component(struct device *dev, struct sdca_function_dat= a *function, + int *num_widgets, int *num_routes) +{ + int i; + + *num_widgets =3D function->num_entities - 1; + *num_routes =3D 0; + + for (i =3D 0; i < function->num_entities - 1; i++) { + struct sdca_entity *entity =3D &function->entities[i]; + + switch (entity->type) { + case SDCA_ENTITY_TYPE_IT: + case SDCA_ENTITY_TYPE_OT: + *num_routes +=3D !!entity->iot.clock; + *num_routes +=3D !!entity->iot.is_dataport; + break; + case SDCA_ENTITY_TYPE_PDE: + *num_routes +=3D entity->pde.num_managed; + break; + default: + break; + } + + *num_routes +=3D entity->num_sources; + + if (entity->group) + (*num_routes)++; + } + + return 0; +} +EXPORT_SYMBOL_NS(sdca_asoc_count_component, "SND_SOC_SDCA"); + +static const char *get_terminal_name(enum sdca_terminal_type type) +{ + switch (type) { + case SDCA_TERM_TYPE_LINEIN_STEREO: + return SDCA_TERM_TYPE_LINEIN_STEREO_NAME; + case SDCA_TERM_TYPE_LINEIN_FRONT_LR: + return SDCA_TERM_TYPE_LINEIN_FRONT_LR_NAME; + case SDCA_TERM_TYPE_LINEIN_CENTER_LFE: + return SDCA_TERM_TYPE_LINEIN_CENTER_LFE_NAME; + case SDCA_TERM_TYPE_LINEIN_SURROUND_LR: + return SDCA_TERM_TYPE_LINEIN_SURROUND_LR_NAME; + case SDCA_TERM_TYPE_LINEIN_REAR_LR: + return SDCA_TERM_TYPE_LINEIN_REAR_LR_NAME; + case SDCA_TERM_TYPE_LINEOUT_STEREO: + return SDCA_TERM_TYPE_LINEOUT_STEREO_NAME; + case SDCA_TERM_TYPE_LINEOUT_FRONT_LR: + return SDCA_TERM_TYPE_LINEOUT_FRONT_LR_NAME; + case SDCA_TERM_TYPE_LINEOUT_CENTER_LFE: + return SDCA_TERM_TYPE_LINEOUT_CENTER_LFE_NAME; + case SDCA_TERM_TYPE_LINEOUT_SURROUND_LR: + return SDCA_TERM_TYPE_LINEOUT_SURROUND_LR_NAME; + case SDCA_TERM_TYPE_LINEOUT_REAR_LR: + return SDCA_TERM_TYPE_LINEOUT_REAR_LR_NAME; + case SDCA_TERM_TYPE_MIC_JACK: + return SDCA_TERM_TYPE_MIC_JACK_NAME; + case SDCA_TERM_TYPE_STEREO_JACK: + return SDCA_TERM_TYPE_STEREO_JACK_NAME; + case SDCA_TERM_TYPE_FRONT_LR_JACK: + return SDCA_TERM_TYPE_FRONT_LR_JACK_NAME; + case SDCA_TERM_TYPE_CENTER_LFE_JACK: + return SDCA_TERM_TYPE_CENTER_LFE_JACK_NAME; + case SDCA_TERM_TYPE_SURROUND_LR_JACK: + return SDCA_TERM_TYPE_SURROUND_LR_JACK_NAME; + case SDCA_TERM_TYPE_REAR_LR_JACK: + return SDCA_TERM_TYPE_REAR_LR_JACK_NAME; + case SDCA_TERM_TYPE_HEADPHONE_JACK: + return SDCA_TERM_TYPE_HEADPHONE_JACK_NAME; + case SDCA_TERM_TYPE_HEADSET_JACK: + return SDCA_TERM_TYPE_HEADSET_JACK_NAME; + default: + return NULL; + } +} + +static int entity_early_parse_ge(struct device *dev, + struct sdca_function_data *function, + struct sdca_entity *entity) +{ + struct sdca_control_range *range; + struct sdca_control *control; + struct snd_kcontrol_new *kctl; + struct soc_enum *soc_enum; + const char *control_name; + unsigned int *values; + const char **texts; + int i; + + control =3D selector_find_control(entity, SDCA_CTL_GE_SELECTED_MODE); + if (!control) { + dev_err(dev, "%s: no selected mode control\n", entity->label); + return -EINVAL; + } + + if (control->layers !=3D SDCA_ACCESS_LAYER_CLASS) + dev_warn(dev, "%s: unexpected access layer: %x\n", + entity->label, control->layers); + + range =3D control_find_range(dev, entity, control, SDCA_SELECTED_MODE_NCO= LS, 0); + if (!range) + return -EINVAL; + + control_name =3D devm_kasprintf(dev, GFP_KERNEL, "%s %s", + entity->label, control->label); + if (!control_name) + return -ENOMEM; + + kctl =3D devm_kmalloc(dev, sizeof(*kctl), GFP_KERNEL); + if (!kctl) + return -ENOMEM; + + soc_enum =3D devm_kmalloc(dev, sizeof(*soc_enum), GFP_KERNEL); + if (!soc_enum) + return -ENOMEM; + + texts =3D devm_kcalloc(dev, range->rows + 3, sizeof(*texts), GFP_KERNEL); + if (!texts) + return -ENOMEM; + + values =3D devm_kcalloc(dev, range->rows + 3, sizeof(*values), GFP_KERNEL= ); + if (!values) + return -ENOMEM; + + texts[0] =3D "No Jack"; + texts[1] =3D "Jack Unknown"; + texts[2] =3D "Detection in Progress"; + values[0] =3D 0; + values[1] =3D 1; + values[2] =3D 2; + for (i =3D 0; i < range->rows; i++) { + enum sdca_terminal_type type; + + type =3D sdca_range(range, SDCA_SELECTED_MODE_TERM_TYPE, i); + + values[i + 3] =3D sdca_range(range, SDCA_SELECTED_MODE_INDEX, i); + texts[i + 3] =3D get_terminal_name(type); + if (!texts[i + 3]) { + dev_err(dev, "%s: Unrecognised terminal type: %#x\n", + entity->label, type); + return -EINVAL; + } + } + + soc_enum->reg =3D SDW_SDCA_CTL(function->desc->adr, entity->id, control->= sel, 0); + soc_enum->items =3D range->rows + 3; + soc_enum->mask =3D roundup_pow_of_two(soc_enum->items) - 1; + soc_enum->texts =3D texts; + + kctl->iface =3D SNDRV_CTL_ELEM_IFACE_MIXER; + kctl->name =3D control_name; + kctl->info =3D snd_soc_info_enum_double; + kctl->get =3D snd_soc_dapm_get_enum_double; + kctl->put =3D snd_soc_dapm_put_enum_double; + kctl->private_value =3D (unsigned long)soc_enum; + + entity->ge.kctl =3D kctl; + + return 0; +} + +static void add_route(struct snd_soc_dapm_route **route, const char *sink, + const char *control, const char *source) +{ + (*route)->sink =3D sink; + (*route)->control =3D control; + (*route)->source =3D source; + (*route)++; +} + +static int entity_parse_simple(struct device *dev, + struct sdca_function_data *function, + struct sdca_entity *entity, + struct snd_soc_dapm_widget **widget, + struct snd_soc_dapm_route **route, + enum snd_soc_dapm_type id) +{ + int i; + + (*widget)->id =3D id; + (*widget)++; + + for (i =3D 0; i < entity->num_sources; i++) + add_route(route, entity->label, NULL, entity->sources[i]->label); + + return 0; +} + +static int entity_parse_it(struct device *dev, + struct sdca_function_data *function, + struct sdca_entity *entity, + struct snd_soc_dapm_widget **widget, + struct snd_soc_dapm_route **route) +{ + int i; + + if (entity->iot.is_dataport) { + const char *aif_name =3D devm_kasprintf(dev, GFP_KERNEL, "%s %s", + entity->label, "Playback"); + if (!aif_name) + return -ENOMEM; + + (*widget)->id =3D snd_soc_dapm_aif_in; + + add_route(route, entity->label, NULL, aif_name); + } else { + (*widget)->id =3D snd_soc_dapm_input; + } + + if (entity->iot.clock) + add_route(route, entity->label, NULL, entity->iot.clock->label); + + for (i =3D 0; i < entity->num_sources; i++) + add_route(route, entity->label, NULL, entity->sources[i]->label); + + (*widget)++; + + return 0; +} + +static int entity_parse_ot(struct device *dev, + struct sdca_function_data *function, + struct sdca_entity *entity, + struct snd_soc_dapm_widget **widget, + struct snd_soc_dapm_route **route) +{ + int i; + + if (entity->iot.is_dataport) { + const char *aif_name =3D devm_kasprintf(dev, GFP_KERNEL, "%s %s", + entity->label, "Capture"); + if (!aif_name) + return -ENOMEM; + + (*widget)->id =3D snd_soc_dapm_aif_out; + + add_route(route, aif_name, NULL, entity->label); + } else { + (*widget)->id =3D snd_soc_dapm_output; + } + + if (entity->iot.clock) + add_route(route, entity->label, NULL, entity->iot.clock->label); + + for (i =3D 0; i < entity->num_sources; i++) + add_route(route, entity->label, NULL, entity->sources[i]->label); + + (*widget)++; + + return 0; +} + +static int entity_pde_event(struct snd_soc_dapm_widget *widget, + struct snd_kcontrol *kctl, int event) +{ + struct snd_soc_component *component =3D widget->dapm->component; + struct sdca_entity *entity =3D widget->priv; + static const int poll_us =3D 10000; + int polls =3D 1; + unsigned int reg, val; + int from, to, i; + int ret; + + if (!component) + return -EIO; + + switch (event) { + case SND_SOC_DAPM_POST_PMD: + from =3D widget->on_val; + to =3D widget->off_val; + break; + case SND_SOC_DAPM_POST_PMU: + from =3D widget->off_val; + to =3D widget->on_val; + break; + } + + for (i =3D 0; i < entity->pde.num_max_delay; i++) { + struct sdca_pde_delay *delay =3D &entity->pde.max_delay[i]; + + if (delay->from_ps =3D=3D from && delay->to_ps =3D=3D to) { + polls =3D max(polls, delay->us / poll_us); + break; + } + } + + reg =3D SDW_SDCA_CTL(SDW_SDCA_CTL_FUNC(widget->reg), + SDW_SDCA_CTL_ENT(widget->reg), + SDCA_CTL_PDE_ACTUAL_PS, 0); + + for (i =3D 0; i < polls; i++) { + if (i) + fsleep(poll_us); + + ret =3D regmap_read(component->regmap, reg, &val); + if (ret) + return ret; + else if (val =3D=3D to) + return 0; + } + + dev_err(component->dev, "%s: power transition failed: %x\n", + entity->label, val); + return -ETIMEDOUT; +} + +static int entity_parse_pde(struct device *dev, + struct sdca_function_data *function, + struct sdca_entity *entity, + struct snd_soc_dapm_widget **widget, + struct snd_soc_dapm_route **route) +{ + unsigned int target =3D (1 << SDCA_PDE_PS0) | (1 << SDCA_PDE_PS3); + struct sdca_control_range *range; + struct sdca_control *control; + unsigned int mask =3D 0; + int i; + + control =3D selector_find_control(entity, SDCA_CTL_PDE_REQUESTED_PS); + if (!control) { + dev_err(dev, "%s: no power control\n", entity->label); + return -EINVAL; + } + + /* Power should only be controlled by the driver */ + if (control->layers !=3D SDCA_ACCESS_LAYER_CLASS) + dev_warn(dev, "%s: unexpected access layer: %x\n", + entity->label, control->layers); + + range =3D control_find_range(dev, entity, control, SDCA_REQUESTED_PS_NCOL= S, 0); + if (!range) + return -EINVAL; + + for (i =3D 0; i < range->rows; i++) + mask |=3D 1 << sdca_range(range, SDCA_REQUESTED_PS_STATE, i); + + if ((mask & target) !=3D target) { + dev_err(dev, "%s: power control missing states\n", entity->label); + return -EINVAL; + } + + (*widget)->id =3D snd_soc_dapm_supply; + (*widget)->reg =3D SDW_SDCA_CTL(function->desc->adr, entity->id, control-= >sel, 0); + (*widget)->mask =3D GENMASK(control->nbits - 1, 0); + (*widget)->on_val =3D SDCA_PDE_PS0; + (*widget)->off_val =3D SDCA_PDE_PS3; + (*widget)->event_flags =3D SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD; + (*widget)->event =3D entity_pde_event; + (*widget)->priv =3D entity; + (*widget)++; + + for (i =3D 0; i < entity->pde.num_managed; i++) + add_route(route, entity->pde.managed[i]->label, NULL, entity->label); + + for (i =3D 0; i < entity->num_sources; i++) + add_route(route, entity->label, NULL, entity->sources[i]->label); + + return 0; +} + +/* Device selector units are controlled through a group entity */ +static int entity_parse_su_device(struct device *dev, + struct sdca_function_data *function, + struct sdca_entity *entity, + struct snd_soc_dapm_widget **widget, + struct snd_soc_dapm_route **route) +{ + struct sdca_control_range *range; + int num_routes =3D 0; + int i, j; + + if (!entity->group) { + dev_err(dev, "%s: device selector unit missing group\n", entity->label); + return -EINVAL; + } + + range =3D selector_find_range(dev, entity->group, SDCA_CTL_GE_SELECTED_MO= DE, + SDCA_SELECTED_MODE_NCOLS, 0); + if (!range) + return -EINVAL; + + (*widget)->id =3D snd_soc_dapm_mux; + (*widget)->kcontrol_news =3D entity->group->ge.kctl; + (*widget)->num_kcontrols =3D 1; + (*widget)++; + + for (i =3D 0; i < entity->group->ge.num_modes; i++) { + struct sdca_ge_mode *mode =3D &entity->group->ge.modes[i]; + + for (j =3D 0; j < mode->num_controls; j++) { + struct sdca_ge_control *affected =3D &mode->controls[j]; + int term; + + if (affected->id !=3D entity->id || + affected->sel !=3D SDCA_CTL_SU_SELECTOR || + !affected->val) + continue; + + if (affected->val - 1 >=3D entity->num_sources) { + dev_err(dev, "%s: Bad control value: %#x\n", + entity->label, affected->val); + return -EINVAL; + } + + if (++num_routes > entity->num_sources) { + dev_err(dev, "%s: Too many input routes\n", entity->label); + return -EINVAL; + } + + term =3D sdca_range_search(range, SDCA_SELECTED_MODE_INDEX, + mode->val, SDCA_SELECTED_MODE_TERM_TYPE); + if (!term) { + dev_err(dev, "%s: Mode not found: %#x\n", + entity->label, mode->val); + return -EINVAL; + } + + add_route(route, entity->label, get_terminal_name(term), + entity->sources[affected->val - 1]->label); + } + } + + return 0; +} + +/* Class selector units will be exported as an ALSA control */ +static int entity_parse_su_class(struct device *dev, + struct sdca_function_data *function, + struct sdca_entity *entity, + struct sdca_control *control, + struct snd_soc_dapm_widget **widget, + struct snd_soc_dapm_route **route) +{ + struct snd_kcontrol_new *kctl; + struct soc_enum *soc_enum; + const char **texts; + int i; + + kctl =3D devm_kmalloc(dev, sizeof(*kctl), GFP_KERNEL); + if (!kctl) + return -ENOMEM; + + soc_enum =3D devm_kmalloc(dev, sizeof(*soc_enum), GFP_KERNEL); + if (!soc_enum) + return -ENOMEM; + + texts =3D devm_kcalloc(dev, entity->num_sources + 1, sizeof(*texts), GFP_= KERNEL); + if (!texts) + return -ENOMEM; + + texts[0] =3D "No Signal"; + for (i =3D 0; i < entity->num_sources; i++) + texts[i + 1] =3D entity->sources[i]->label; + + soc_enum->reg =3D SDW_SDCA_CTL(function->desc->adr, entity->id, control->= sel, 0); + soc_enum->items =3D entity->num_sources + 1; + soc_enum->mask =3D roundup_pow_of_two(soc_enum->items) - 1; + soc_enum->texts =3D texts; + + kctl->iface =3D SNDRV_CTL_ELEM_IFACE_MIXER; + kctl->name =3D "Route"; + kctl->info =3D snd_soc_info_enum_double; + kctl->get =3D snd_soc_dapm_get_enum_double; + kctl->put =3D snd_soc_dapm_put_enum_double; + kctl->private_value =3D (unsigned long)soc_enum; + + (*widget)->id =3D snd_soc_dapm_mux; + (*widget)->kcontrol_news =3D kctl; + (*widget)->num_kcontrols =3D 1; + (*widget)++; + + for (i =3D 0; i < entity->num_sources; i++) + add_route(route, entity->label, texts[i + 1], entity->sources[i]->label); + + return 0; +} + +static int entity_parse_su(struct device *dev, + struct sdca_function_data *function, + struct sdca_entity *entity, + struct snd_soc_dapm_widget **widget, + struct snd_soc_dapm_route **route) +{ + struct sdca_control *control; + + if (!entity->num_sources) { + dev_err(dev, "%s: selector with no inputs\n", entity->label); + return -EINVAL; + } + + control =3D selector_find_control(entity, SDCA_CTL_SU_SELECTOR); + if (!control) { + dev_err(dev, "%s: no selector control\n", entity->label); + return -EINVAL; + } + + if (control->layers =3D=3D SDCA_ACCESS_LAYER_DEVICE) + return entity_parse_su_device(dev, function, entity, widget, route); + + if (control->layers !=3D SDCA_ACCESS_LAYER_CLASS) + dev_warn(dev, "%s: unexpected access layer: %x\n", + entity->label, control->layers); + + return entity_parse_su_class(dev, function, entity, control, widget, rout= e); +} + +static int entity_parse_mu(struct device *dev, + struct sdca_function_data *function, + struct sdca_entity *entity, + struct snd_soc_dapm_widget **widget, + struct snd_soc_dapm_route **route) +{ + struct sdca_control *control; + struct snd_kcontrol_new *kctl; + int cn; + int i; + + if (!entity->num_sources) { + dev_err(dev, "%s: selector 1 or more inputs\n", entity->label); + return -EINVAL; + } + + control =3D selector_find_control(entity, SDCA_CTL_MU_MIXER); + if (!control) { + dev_err(dev, "%s: no mixer controls\n", entity->label); + return -EINVAL; + } + + /* MU control should be through DAPM */ + if (control->layers !=3D SDCA_ACCESS_LAYER_CLASS) + dev_warn(dev, "%s: unexpected access layer: %x\n", + entity->label, control->layers); + + if (entity->num_sources !=3D hweight64(control->cn_list)) { + dev_err(dev, "%s: mismatched control and sources\n", entity->label); + return -EINVAL; + } + + kctl =3D devm_kcalloc(dev, entity->num_sources, sizeof(*kctl), GFP_KERNEL= ); + if (!kctl) + return -ENOMEM; + + i =3D 0; + for_each_set_bit(cn, (unsigned long *)&control->cn_list, + BITS_PER_TYPE(control->cn_list)) { + const char *control_name; + struct soc_mixer_control *mc; + + control_name =3D devm_kasprintf(dev, GFP_KERNEL, "%s %d", + control->label, i + 1); + if (!control_name) + return -ENOMEM; + + mc =3D devm_kmalloc(dev, sizeof(*mc), GFP_KERNEL); + if (!mc) + return -ENOMEM; + + mc->reg =3D SND_SOC_NOPM; + mc->rreg =3D SND_SOC_NOPM; + mc->invert =3D 1; // Ensure default is connected + mc->min =3D 0; + mc->max =3D 1; + + kctl[i].name =3D control_name; + kctl[i].private_value =3D (unsigned long)mc; + kctl[i].iface =3D SNDRV_CTL_ELEM_IFACE_MIXER; + kctl[i].info =3D snd_soc_info_volsw; + kctl[i].get =3D snd_soc_dapm_get_volsw; + kctl[i].put =3D snd_soc_dapm_put_volsw; + i++; + } + + (*widget)->id =3D snd_soc_dapm_mixer; + (*widget)->kcontrol_news =3D kctl; + (*widget)->num_kcontrols =3D entity->num_sources; + (*widget)++; + + for (i =3D 0; i < entity->num_sources; i++) + add_route(route, entity->label, kctl[i].name, entity->sources[i]->label); + + return 0; +} + +static int entity_cs_event(struct snd_soc_dapm_widget *widget, + struct snd_kcontrol *kctl, int event) +{ + struct snd_soc_component *component =3D widget->dapm->component; + struct sdca_entity *entity =3D widget->priv; + + if (!component) + return -EIO; + + if (entity->cs.max_delay) + fsleep(entity->cs.max_delay); + + return 0; +} + +static int entity_parse_cs(struct device *dev, + struct sdca_function_data *function, + struct sdca_entity *entity, + struct snd_soc_dapm_widget **widget, + struct snd_soc_dapm_route **route) +{ + int i; + + (*widget)->id =3D snd_soc_dapm_supply; + (*widget)->subseq =3D 1; /* Ensure these run after PDEs */ + (*widget)->event_flags =3D SND_SOC_DAPM_POST_PMU; + (*widget)->event =3D entity_cs_event; + (*widget)->priv =3D entity; + (*widget)++; + + for (i =3D 0; i < entity->num_sources; i++) + add_route(route, entity->label, NULL, entity->sources[i]->label); + + return 0; +} + +int sdca_asoc_populate_dapm(struct device *dev, struct sdca_function_data = *function, + struct snd_soc_dapm_widget *widget, + struct snd_soc_dapm_route *route) +{ + int ret; + int i; + + /* Some entities need to add controls referenced by other entities */ + for (i =3D 0; i < function->num_entities - 1; i++) { + struct sdca_entity *entity =3D &function->entities[i]; + + switch (entity->type) { + case SDCA_ENTITY_TYPE_GE: + ret =3D entity_early_parse_ge(dev, function, entity); + if (ret) + return ret; + break; + default: + break; + } + } + + for (i =3D 0; i < function->num_entities - 1; i++) { + struct sdca_entity *entity =3D &function->entities[i]; + + widget->name =3D entity->label; + widget->reg =3D SND_SOC_NOPM; + + switch (entity->type) { + case SDCA_ENTITY_TYPE_IT: + ret =3D entity_parse_it(dev, function, entity, &widget, &route); + break; + case SDCA_ENTITY_TYPE_OT: + ret =3D entity_parse_ot(dev, function, entity, &widget, &route); + break; + case SDCA_ENTITY_TYPE_PDE: + ret =3D entity_parse_pde(dev, function, entity, &widget, &route); + break; + case SDCA_ENTITY_TYPE_SU: + ret =3D entity_parse_su(dev, function, entity, &widget, &route); + break; + case SDCA_ENTITY_TYPE_MU: + ret =3D entity_parse_mu(dev, function, entity, &widget, &route); + break; + case SDCA_ENTITY_TYPE_CS: + ret =3D entity_parse_cs(dev, function, entity, &widget, &route); + break; + case SDCA_ENTITY_TYPE_CX: + /* + * FIXME: For now we will just treat these as a supply, + * meaning all options are enabled. + */ + dev_warn(dev, "%s: Clock Selectors not fully supported yet\n", + entity->label); + ret =3D entity_parse_simple(dev, function, entity, &widget, + &route, snd_soc_dapm_supply); + break; + case SDCA_ENTITY_TYPE_TG: + ret =3D entity_parse_simple(dev, function, entity, &widget, + &route, snd_soc_dapm_siggen); + break; + case SDCA_ENTITY_TYPE_GE: + ret =3D entity_parse_simple(dev, function, entity, &widget, + &route, snd_soc_dapm_supply); + break; + default: + ret =3D entity_parse_simple(dev, function, entity, &widget, + &route, snd_soc_dapm_pga); + break; + } + if (ret) + return ret; + + if (entity->group) + add_route(&route, entity->label, NULL, entity->group->label); + } + + return 0; +} +EXPORT_SYMBOL_NS(sdca_asoc_populate_dapm, "SND_SOC_SDCA"); + +int sdca_asoc_populate_component(struct device *dev, + struct sdca_function_data *function, + struct snd_soc_component_driver *component_drv) +{ + struct snd_soc_dapm_widget *widgets; + struct snd_soc_dapm_route *routes; + int num_widgets, num_routes; + int ret; + + ret =3D sdca_asoc_count_component(dev, function, &num_widgets, &num_route= s); + if (ret) + return ret; + + widgets =3D devm_kcalloc(dev, num_widgets, sizeof(*widgets), GFP_KERNEL); + if (!widgets) + return -ENOMEM; + + routes =3D devm_kcalloc(dev, num_routes, sizeof(*routes), GFP_KERNEL); + if (!routes) + return -ENOMEM; + + ret =3D sdca_asoc_populate_dapm(dev, function, widgets, routes); + if (ret) + return ret; + + component_drv->dapm_widgets =3D widgets; + component_drv->num_dapm_widgets =3D num_widgets; + component_drv->dapm_routes =3D routes; + component_drv->num_dapm_routes =3D num_routes; + + return 0; +} +EXPORT_SYMBOL_NS(sdca_asoc_populate_component, "SND_SOC_SDCA"); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SDCA library"); --=20 2.39.5 From nobody Wed Dec 17 07:35:00 2025 Received: from mx0b-001ae601.pphosted.com (mx0a-001ae601.pphosted.com [67.231.149.25]) (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 ACE3C2080E7; Thu, 27 Mar 2025 13:00:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=67.231.149.25 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1743080444; cv=fail; b=vBqV+omMA/y3Xmc5ljI7pp6x90XyfLESAVWi0XgMGAGFYUbO+VTZlEjv8jft0OKU+qNg8ZsNKBYMMSyfD0xCcWVYffpQ+8SWQv3yIVrGRG9KjJZl5ZQJJqJlqxxdlOkW59cPPqu/EfUIb1dOWnjFmP/Oh3pFXpNb1kK8/DVbObM= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1743080444; c=relaxed/simple; bh=tJG2Sazi0BUo74qTxyNbNJ/fyybb4EgGNdaeyZfHaaU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=d3mxLEK+qd1r9/CcqVuV+C/xzWdZcQ0VAElxPfCjlfVmVuNo4lWsvQfHSTJ3fGiWqIOXP5eua3LOg+za2d4wRz8z8k0w4bBsncSfrBUwVCvX6ISK1rCw4nwM52lK5ZBZYgXyX+GjWnCapZqw1xzfabm9Vq+BhEdFNNhG4BN0Ksw= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=opensource.cirrus.com; spf=pass smtp.mailfrom=cirrus.com; dkim=pass (2048-bit key) header.d=cirrus.com header.i=@cirrus.com header.b=NlY3hP6P; dkim=pass (1024-bit key) header.d=cirrus4.onmicrosoft.com header.i=@cirrus4.onmicrosoft.com header.b=YIRKOeGJ; arc=fail smtp.client-ip=67.231.149.25 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=opensource.cirrus.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=cirrus.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=cirrus.com header.i=@cirrus.com header.b="NlY3hP6P"; dkim=pass (1024-bit key) header.d=cirrus4.onmicrosoft.com header.i=@cirrus4.onmicrosoft.com header.b="YIRKOeGJ" Received: from pps.filterd (m0077473.ppops.net [127.0.0.1]) by mx0a-001ae601.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 52R8ZkpT032284; Thu, 27 Mar 2025 08:00:24 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cirrus.com; h=cc :content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s= PODMain02222019; bh=l3EaKWMZkap09D7CsI56PjE5mCECqTX8c45Loa9lEUI=; b= NlY3hP6PaEj/6dWKhiM9dXqrcWs0vr+8YEI3ohPInfWfYu1kMhfmKaRqNxbBVVU9 BAlkL4h5CnsKcybHbyqDCUVwG8UjU5vQMyQKBr+DvThDsgHlfcgkXuy+DHquC92F KLfKmZhQw5KWXS3f6HPBdxYY9oZ2gbAkdlM9QUx2gtsaiIUar41y7uxMyjKzAPB/ jhnFJeJS6aaPt8nY9JsH+g+qcmqnD8Ue5hUi3NZHQ4QgwjconSz9AN9yU8DNrIuj TUm/lnaVEDVyS15SRHhghJLva3jPFu3Hh3RNH8S/NihMuPFeFpfelo0xe+ycSqkm 9Td/fEOCkUhj58lNrWRMJw== Received: from nam04-dm6-obe.outbound.protection.outlook.com (mail-dm6nam04lp2045.outbound.protection.outlook.com [104.47.73.45]) by mx0a-001ae601.pphosted.com (PPS) with ESMTPS id 45kmrgvq53-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Thu, 27 Mar 2025 08:00:23 -0500 (CDT) ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=EgYm4cuJPwCULu8sryV3cQeukkDX7+PLe6CKaBfjAq1A5J3u7LcBW8eG5sGiDbXBR5o3O2rgGe+I7voP/NBhHR2P1gFtjOSvW/nIEiciIn+0jmEY1kfIv+vrAGBb+dlF/HbuS0j7+BdfkVaM6ronpyT4MSd1V4mc1mf2Qrknf7Oz7t0YTOUmHJD0RmpvEIr75ldheuPI/ijRHmBZrSy3rPYqbhJqdPsucq36W7q9B3YFYF2hFsY2ShaqeK0YHo5GvEJgUYVB8OP5PRvvTCHb/ZTP3SorpeHF2z5CiAS/K3PBvrynoRn9qYQWRL5Z/PHVRBdXdicFVhI1b5gVPUHmlA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=l3EaKWMZkap09D7CsI56PjE5mCECqTX8c45Loa9lEUI=; b=D0AlbCc4Q2DF6ziU64HXk7LxfNke+/LbpcchlFBkvbWBNLx72gEYy5IDOtEweAM+31mQ5WM+lS7iLywMRyGhDxLGt5wtOD5sMHambFMXJQhZ2DhlF+GC07OzigZgMsHqHEWtq1wRIph/GamHWEZ3Jsj0IIQZ5lARf3eP5Oye7cBzZ54YtwPBcVIF1ePZ9WkvOscIv8kPbyD/SVvwsxJTb/I+dMAVsxv9bu7AQm/+3NkKbhDLEu2Hrqtn7DCegNLMu0mNSPsd4SmcEmoUe73WGxTR2/GUy/4OnJD1pL5tifP7F+rExPGgqo5QVutSnq1pxGQ7/ZGUaRyqwfd5q/oQLw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=fail (sender ip is 84.19.233.75) smtp.rcpttodomain=cirrus.com smtp.mailfrom=cirrus.com; dmarc=fail (p=reject sp=reject pct=100) action=oreject header.from=opensource.cirrus.com; dkim=none (message not signed); arc=none (0) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cirrus4.onmicrosoft.com; s=selector2-cirrus4-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=l3EaKWMZkap09D7CsI56PjE5mCECqTX8c45Loa9lEUI=; b=YIRKOeGJdjBdXWBe832s+0OkxCM+qR+P3IpXmggHlnz2ywBZsuGl7SoV3yTK+hJu2NsE3eMO6AA1BpbwAXs1CRqcdI4uvq56LdGUumieqKx+6tyMHvCY+TIpS/LPVtclU0xN/ObKwW3ZFOpGn6HSANUWF7GNtc0hx0ORnDOIlxE= Received: from CH0PR07CA0018.namprd07.prod.outlook.com (2603:10b6:610:32::23) by SA1PR19MB6870.namprd19.prod.outlook.com (2603:10b6:806:257::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8534.44; Thu, 27 Mar 2025 13:00:15 +0000 Received: from CH2PEPF0000009A.namprd02.prod.outlook.com (2603:10b6:610:32:cafe::3) by CH0PR07CA0018.outlook.office365.com (2603:10b6:610:32::23) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.20.8534.44 via Frontend Transport; Thu, 27 Mar 2025 13:00:15 +0000 X-MS-Exchange-Authentication-Results: spf=fail (sender IP is 84.19.233.75) smtp.mailfrom=cirrus.com; dkim=none (message not signed) header.d=none;dmarc=fail action=oreject header.from=opensource.cirrus.com; Received-SPF: Fail (protection.outlook.com: domain of cirrus.com does not designate 84.19.233.75 as permitted sender) receiver=protection.outlook.com; client-ip=84.19.233.75; helo=edirelay1.ad.cirrus.com; Received: from edirelay1.ad.cirrus.com (84.19.233.75) by CH2PEPF0000009A.mail.protection.outlook.com (10.167.244.22) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.20.8534.20 via Frontend Transport; Thu, 27 Mar 2025 13:00:14 +0000 Received: from ediswmail9.ad.cirrus.com (ediswmail9.ad.cirrus.com [198.61.86.93]) by edirelay1.ad.cirrus.com (Postfix) with ESMTPS id 00197406543; Thu, 27 Mar 2025 13:00:13 +0000 (UTC) Received: from ediswws07.ad.cirrus.com (ediswws07.ad.cirrus.com [198.90.208.14]) by ediswmail9.ad.cirrus.com (Postfix) with ESMTP id CAC7882255D; Thu, 27 Mar 2025 13:00:12 +0000 (UTC) From: Charles Keepax To: broonie@kernel.org Cc: lgirdwood@gmail.com, pierre-louis.bossart@linux.dev, yung-chuan.liao@linux.intel.com, peter.ujfalusi@linux.intel.com, linux-sound@vger.kernel.org, linux-kernel@vger.kernel.org, patches@opensource.cirrus.com Subject: [PATCH v2 2/3] ASoC: SDCA: Create ALSA controls from DisCo Date: Thu, 27 Mar 2025 13:00:11 +0000 Message-Id: <20250327130012.3378732-3-ckeepax@opensource.cirrus.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250327130012.3378732-1-ckeepax@opensource.cirrus.com> References: <20250327130012.3378732-1-ckeepax@opensource.cirrus.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: CH2PEPF0000009A:EE_|SA1PR19MB6870:EE_ X-MS-Office365-Filtering-Correlation-Id: c145f8ed-07a4-402c-c38f-08dd6d2f50cd X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|61400799027|36860700013|376014|82310400026; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?/LVO5irCTd4OBNTTiYWuTcoJyxuH7g7Rs0cVo2IKm/0LpE1/+ER58rRa4Lv+?= =?us-ascii?Q?D5c3SpOKqBda5oP4HLrXl/2xWs2loRF29cTuGXZhyYQ9C8xlgNyXHSmN/E8m?= =?us-ascii?Q?cLGrMJyf+DDijMiveMwrfmEL9BMxBItlkuZ70MXoCRol1DKnJX4RL9wUbbgq?= =?us-ascii?Q?atf1ZKOgPWkLsj+gaciT24nQBllvp7pW/V4sH1bdbsmSx8kIOTEjqpegi7BU?= =?us-ascii?Q?nqpwJbfcEGcn8g9s1coHAIor1YhH0fKztOdr3r8lE2gLo8EQCnyIdhsf8d8r?= =?us-ascii?Q?x9Na+dX44K5KWcEoVAM1f//k9seOOBoUzb3kIiSHNyPO2i6V+0fNM0MuiHGl?= =?us-ascii?Q?YuVjghNwZCmcLiCtX0MRLreZK9NpsN7PH7rqFb8ysRh+bRW3hrwlHvYJGBrl?= =?us-ascii?Q?Ixqok/lrgJDxz4sk7plFZxF2a+3jxJYonV1CxpRBUgZqybuGDm9sFxG8ij9U?= =?us-ascii?Q?54hP/Qxedk/QaVqngPgI0gIvw0Y75puuNh8upWwlZc0+id/UowsB44Vw92pJ?= =?us-ascii?Q?dmdxiwvOGrdK2qq4HJoWihRQ4/3EW+VFG1CfJE+AbLqK4q/Z8kJII1zjK2LS?= =?us-ascii?Q?zuMxWaUZ1jVrr/YNXYH4WQ8Ao64BjZkMCmE2+xmYKCTgI224ehyuBMa5L+9B?= =?us-ascii?Q?cR9Uagzgtjlw89q2u6syn4WMU4H+XAspXcjLZKe6VgyIeuYwXYlnvOnF5Ttz?= =?us-ascii?Q?ULVtNjdX2axnwY5za6od6opPKIaYW24OTTXVNGo+je527EsJXLem3r3tE8RV?= =?us-ascii?Q?XJsvp34WdwatACszErkCcUj3UhA47c9HJ8n5/RFYdpWEiDyovNihU2UY2mH+?= =?us-ascii?Q?5zrJeVQLiX5HJf5jFXY1KULGAZ22ePVWQQzqthC9nHSW+IgphouctqvlwnL9?= =?us-ascii?Q?T9XgpZceDoQZEiDmiJu4U6JhXOTzySYjyM/+diQhHTw7l9CuPBw8zTKi0Nnm?= =?us-ascii?Q?rGQg/EBCZ8w2VxVP2xsR4Fgwp/uI594BeWfO9w1enFQNbkxKvXD9BE/RlCCP?= =?us-ascii?Q?8BMMe4JpilinCWawz4eno9tmgAhWAOn5PVVn172g2pfrpJRa7D5CRHPRrjHk?= =?us-ascii?Q?zJwZi0vTTptKoAWdVmrlN+SfRkrrmEDJgKA94kOjTzz1cc6jITWXOZTkQsu+?= =?us-ascii?Q?D7Jiztv3yDemH7S+0kEeijjiOx31UeVW2PWCTSETmZhszKKoLCZOCTro29E6?= =?us-ascii?Q?JAY6uZN/VvQFJlPHUvdqij0xJs1E9wUlj3N/LK79fSCNDFg8fk0IV8K6780L?= =?us-ascii?Q?2f4fuy7mLN4oz5kSlguFpCsR81TaFn6YFx/gX3Q+nXM6kYPtXgDbqZLpU8lH?= =?us-ascii?Q?VhOFIUVbO22saoebvcrousM1/AUd/e7sFYvgKSB4Ph+B5oorf6BGD8KcUAfz?= =?us-ascii?Q?6BVHPSUVpmVciCfZmv9Dqj9jMZ64gIRCtG7qd1yC1qe+Sz0nulAkGpBY5sra?= =?us-ascii?Q?vXu3H8T+c7hrX9u0b+1/brKUC+lhgEeNUhZPdbPnet+iersW/p/fk2ZQyRfA?= =?us-ascii?Q?DmqtzefWQA565Zo=3D?= X-Forefront-Antispam-Report: CIP:84.19.233.75;CTRY:GB;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:edirelay1.ad.cirrus.com;PTR:ErrorRetry;CAT:NONE;SFS:(13230040)(61400799027)(36860700013)(376014)(82310400026);DIR:OUT;SFP:1102; X-OriginatorOrg: opensource.cirrus.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 27 Mar 2025 13:00:14.0531 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: c145f8ed-07a4-402c-c38f-08dd6d2f50cd X-MS-Exchange-CrossTenant-Id: bec09025-e5bc-40d1-a355-8e955c307de8 X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=bec09025-e5bc-40d1-a355-8e955c307de8;Ip=[84.19.233.75];Helo=[edirelay1.ad.cirrus.com] X-MS-Exchange-CrossTenant-AuthSource: CH2PEPF0000009A.namprd02.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: SA1PR19MB6870 X-Authority-Analysis: v=2.4 cv=GrdC+l1C c=1 sm=1 tr=0 ts=67e54be7 cx=c_pps a=o99l/OlIsmxthp48M8gYaQ==:117 a=h1hSm8JtM9GN1ddwPAif2w==:17 a=wKuvFiaSGQ0qltdbU6+NXLB8nM8=:19 a=Ol13hO9ccFRV9qXi2t6ftBPywas=:19 a=Vs1iUdzkB0EA:10 a=RWc_ulEos4gA:10 a=w1d2syhTAAAA:8 a=x5gQ8A518QfWKtYHqdsA:9 a=BGLuxUZjE2igh1l4FkT-:22 X-Proofpoint-GUID: oTW3JEk89FJ0wkylzs24dC6uK91evQff X-Proofpoint-ORIG-GUID: oTW3JEk89FJ0wkylzs24dC6uK91evQff X-Proofpoint-Spam-Reason: safe Content-Type: text/plain; charset="utf-8" Use the previously parsed DisCo information from ACPI to create the ALSA controls required by an SDCA Function. This maps all User and Application level SDCA Controls to ALSA controls. Typically controls marked with those access levels are just volumes and mutes. SDCA defines volume controls as an integer in 1/256ths of a dB and then provides a mechanism to specify what values are valid (range templates). Currently only a simple case of a single linear volume range with a power of 2 step size is supported. This allows the code to expose the volume control using a simple shift. This will need expanded in the future, to support more complex ranges and probably also some additional control types but this should be sufficient to for a first pass. Signed-off-by: Charles Keepax --- No change since v1. include/sound/sdca_asoc.h | 6 +- include/sound/sdca_function.h | 10 ++ sound/soc/sdca/sdca_asoc.c | 191 +++++++++++++++++++++++++++++++++- 3 files changed, 202 insertions(+), 5 deletions(-) diff --git a/include/sound/sdca_asoc.h b/include/sound/sdca_asoc.h index 414d461b6fc4..d19e7e969283 100644 --- a/include/sound/sdca_asoc.h +++ b/include/sound/sdca_asoc.h @@ -12,16 +12,20 @@ =20 struct device; struct sdca_function_data; +struct snd_kcontrol_new; struct snd_soc_component_driver; struct snd_soc_dapm_route; struct snd_soc_dapm_widget; =20 int sdca_asoc_count_component(struct device *dev, struct sdca_function_dat= a *function, - int *num_widgets, int *num_routes); + int *num_widgets, int *num_routes, int *num_controls); =20 int sdca_asoc_populate_dapm(struct device *dev, struct sdca_function_data = *function, struct snd_soc_dapm_widget *widgets, struct snd_soc_dapm_route *routes); +int sdca_asoc_populate_controls(struct device *dev, + struct sdca_function_data *function, + struct snd_kcontrol_new *kctl); =20 int sdca_asoc_populate_component(struct device *dev, struct sdca_function_data *function, diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h index 83fedc39cf71..77ffb1f4e1ca 100644 --- a/include/sound/sdca_function.h +++ b/include/sound/sdca_function.h @@ -206,6 +206,16 @@ enum sdca_fu_controls { SDCA_CTL_FU_LATENCY =3D 0x10, }; =20 +/** + * enum sdca_volume_range - Column definitions for Q7.8dB volumes/gains + */ +enum sdca_volume_range { + SDCA_VOLUME_LINEAR_MIN =3D 0, + SDCA_VOLUME_LINEAR_MAX =3D 1, + SDCA_VOLUME_LINEAR_STEP =3D 2, + SDCA_VOLUME_LINEAR_NCOLS =3D 3, +}; + /** * enum sdca_xu_controls - SDCA Controls for Extension Unit * diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index c91660f8dd4f..a3184189eda0 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -21,6 +21,7 @@ #include #include #include +#include =20 static struct sdca_control *selector_find_control(struct sdca_entity *enti= ty, const int sel) @@ -69,13 +70,24 @@ static struct sdca_control_range *selector_find_range(s= truct device *dev, return control_find_range(dev, entity, control, cols, rows); } =20 +static bool exported_control(struct sdca_control *control) +{ + /* No need to export control for something that only has one value */ + if (control->has_fixed) + return false; + + return control->layers & (SDCA_ACCESS_LAYER_USER | + SDCA_ACCESS_LAYER_APPLICATION); +} + int sdca_asoc_count_component(struct device *dev, struct sdca_function_dat= a *function, - int *num_widgets, int *num_routes) + int *num_widgets, int *num_routes, int *num_controls) { - int i; + int i, j; =20 *num_widgets =3D function->num_entities - 1; *num_routes =3D 0; + *num_controls =3D 0; =20 for (i =3D 0; i < function->num_entities - 1; i++) { struct sdca_entity *entity =3D &function->entities[i]; @@ -97,6 +109,11 @@ int sdca_asoc_count_component(struct device *dev, struc= t sdca_function_data *fun =20 if (entity->group) (*num_routes)++; + + for (j =3D 0; j < entity->num_controls; j++) { + if (exported_control(&entity->controls[j])) + (*num_controls)++; + } } =20 return 0; @@ -771,16 +788,172 @@ int sdca_asoc_populate_dapm(struct device *dev, stru= ct sdca_function_data *funct } EXPORT_SYMBOL_NS(sdca_asoc_populate_dapm, "SND_SOC_SDCA"); =20 +static int control_limit_kctl(struct device *dev, + struct sdca_entity *entity, + struct sdca_control *control, + struct snd_kcontrol_new *kctl) +{ + struct soc_mixer_control *mc =3D (struct soc_mixer_control *)kctl->privat= e_value; + struct sdca_control_range *range; + int min, max, step; + unsigned int *tlv; + int shift; + + if (control->type !=3D SDCA_CTL_DATATYPE_Q7P8DB) + return 0; + + /* + * FIXME: For now only handle the simple case of a single linear range + */ + range =3D control_find_range(dev, entity, control, SDCA_VOLUME_LINEAR_NCO= LS, 1); + if (!range) + return -EINVAL; + + min =3D sdca_range(range, SDCA_VOLUME_LINEAR_MIN, 0); + max =3D sdca_range(range, SDCA_VOLUME_LINEAR_MAX, 0); + step =3D sdca_range(range, SDCA_VOLUME_LINEAR_STEP, 0); + + min =3D sign_extend32(min, control->nbits - 1); + max =3D sign_extend32(max, control->nbits - 1); + + /* + * FIXME: Only support power of 2 step sizes as this can be supported + * by a simple shift. + */ + if (hweight32(step) !=3D 1) { + dev_err(dev, "%s: %s: currently unsupported step size\n", + entity->label, control->label); + return -EINVAL; + } + + /* + * The SDCA volumes are in steps of 1/256th of a dB, a step down of + * 64 (shift of 6) gives 1/4dB. 1/4dB is the smallest unit that is also + * representable in the ALSA TLVs which are in 1/100ths of a dB. + */ + shift =3D max(ffs(step) - 1, 6); + + tlv =3D devm_kcalloc(dev, 4, sizeof(*tlv), GFP_KERNEL); + if (!tlv) + return -ENOMEM; + + tlv[0] =3D SNDRV_CTL_TLVT_DB_SCALE; + tlv[1] =3D 2 * sizeof(*tlv); + tlv[2] =3D (min * 100) >> 8; + tlv[3] =3D ((1 << shift) * 100) >> 8; + + mc->min =3D min >> shift; + mc->max =3D max >> shift; + mc->shift =3D shift; + mc->rshift =3D shift; + mc->sign_bit =3D 15 - shift; + + kctl->access =3D SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_R= EADWRITE; + kctl->tlv.p =3D tlv; + + return 0; +} + +static int populate_control(struct device *dev, + struct sdca_function_data *function, + struct sdca_entity *entity, + struct sdca_control *control, + struct snd_kcontrol_new **kctl) +{ + const char *control_suffix =3D ""; + const char *control_name; + struct soc_mixer_control *mc; + int index =3D 0; + int ret; + int cn; + + if (!exported_control(control)) + return 0; + + if (control->type =3D=3D SDCA_CTL_DATATYPE_ONEBIT) + control_suffix =3D " Switch"; + + control_name =3D devm_kasprintf(dev, GFP_KERNEL, "%s %s%s", entity->label, + control->label, control_suffix); + if (!control_name) + return -ENOMEM; + + mc =3D devm_kmalloc(dev, sizeof(*mc), GFP_KERNEL); + if (!mc) + return -ENOMEM; + + for_each_set_bit(cn, (unsigned long *)&control->cn_list, + BITS_PER_TYPE(control->cn_list)) { + switch (index++) { + case 0: + mc->reg =3D SDW_SDCA_CTL(function->desc->adr, entity->id, + control->sel, cn); + mc->rreg =3D mc->reg; + break; + case 1: + mc->rreg =3D SDW_SDCA_CTL(function->desc->adr, entity->id, + control->sel, cn); + break; + default: + dev_err(dev, "%s: %s: only mono/stereo controls supported\n", + entity->label, control->label); + return -EINVAL; + } + } + + mc->min =3D 0; + mc->max =3D (0x1ull << control->nbits) - 1; + + (*kctl)->name =3D control_name; + (*kctl)->private_value =3D (unsigned long)mc; + (*kctl)->iface =3D SNDRV_CTL_ELEM_IFACE_MIXER; + (*kctl)->info =3D snd_soc_info_volsw; + (*kctl)->get =3D snd_soc_get_volsw; + (*kctl)->put =3D snd_soc_put_volsw; + + ret =3D control_limit_kctl(dev, entity, control, *kctl); + if (ret) + return ret; + + (*kctl)++; + + return 0; +} + +int sdca_asoc_populate_controls(struct device *dev, + struct sdca_function_data *function, + struct snd_kcontrol_new *kctl) +{ + int i, j; + int ret; + + for (i =3D 0; i < function->num_entities; i++) { + struct sdca_entity *entity =3D &function->entities[i]; + + for (j =3D 0; j < entity->num_controls; j++) { + ret =3D populate_control(dev, function, entity, + &entity->controls[j], &kctl); + if (ret) + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_NS(sdca_asoc_populate_controls, "SND_SOC_SDCA"); + int sdca_asoc_populate_component(struct device *dev, struct sdca_function_data *function, struct snd_soc_component_driver *component_drv) { struct snd_soc_dapm_widget *widgets; struct snd_soc_dapm_route *routes; - int num_widgets, num_routes; + struct snd_kcontrol_new *controls; + int num_widgets, num_routes, num_controls; int ret; =20 - ret =3D sdca_asoc_count_component(dev, function, &num_widgets, &num_route= s); + ret =3D sdca_asoc_count_component(dev, function, &num_widgets, &num_route= s, + &num_controls); if (ret) return ret; =20 @@ -792,14 +965,24 @@ int sdca_asoc_populate_component(struct device *dev, if (!routes) return -ENOMEM; =20 + controls =3D devm_kcalloc(dev, num_controls, sizeof(*controls), GFP_KERNE= L); + if (!controls) + return -ENOMEM; + ret =3D sdca_asoc_populate_dapm(dev, function, widgets, routes); if (ret) return ret; =20 + ret =3D sdca_asoc_populate_controls(dev, function, controls); + if (ret) + return ret; + component_drv->dapm_widgets =3D widgets; component_drv->num_dapm_widgets =3D num_widgets; component_drv->dapm_routes =3D routes; component_drv->num_dapm_routes =3D num_routes; + component_drv->controls =3D controls; + component_drv->num_controls =3D num_controls; =20 return 0; } --=20 2.39.5 From nobody Wed Dec 17 07:35:00 2025 Received: from mx0b-001ae601.pphosted.com (mx0a-001ae601.pphosted.com [67.231.149.25]) (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 C6F1C215769; Thu, 27 Mar 2025 13:00:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=67.231.149.25 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1743080449; cv=fail; b=j/w2Z9pL5Wc5Y/dklbLZ5QKEmDK0viMlS+kddoyoZb7dLoTToZ58kaaMhWsp60IhigkdFFyRR7/bnL9N51oiG9PLmryCCoDmebC3hUgiXj/RMrGFqFrSS9680jzHEjMb3Ju27z+r/XS9MBF1wXbLJe5oe0Auc+nocWWmiyS5vDU= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1743080449; c=relaxed/simple; bh=tiBEVFcc7w1I3hGBVmM8wE+t+fivCS1kO5FZA9jrO5Q=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=cUe0kaPRnx7sONDg7JfOYiU3D7ahkgFoQmnTsNgn2xFwlMYLSiXOvsfC9BKxcHa9S5ZGNe0fy+Ah5RaGIO2SJThaWyopzAoFbwaAZSMvkznnhaWhoDZ2FGtAz8JV1V5U/1RzBiOUlZnzV/RC8GOuWLFp9Mw1CT3HbTBMPhNtH/0= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=opensource.cirrus.com; spf=pass smtp.mailfrom=cirrus.com; dkim=pass (2048-bit key) header.d=cirrus.com header.i=@cirrus.com header.b=hwsI4eJq; dkim=pass (1024-bit key) header.d=cirrus4.onmicrosoft.com header.i=@cirrus4.onmicrosoft.com header.b=h0yGYe4k; arc=fail smtp.client-ip=67.231.149.25 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=opensource.cirrus.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=cirrus.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=cirrus.com header.i=@cirrus.com header.b="hwsI4eJq"; dkim=pass (1024-bit key) header.d=cirrus4.onmicrosoft.com header.i=@cirrus4.onmicrosoft.com header.b="h0yGYe4k" Received: from pps.filterd (m0077473.ppops.net [127.0.0.1]) by mx0a-001ae601.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 52R8ZkpS032284; Thu, 27 Mar 2025 08:00:18 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cirrus.com; h=cc :content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s= PODMain02222019; bh=uaxN9Z6jdGB2UvOdNwa49OUT9bPA2k9j5KAeIDEq6yA=; b= hwsI4eJqnWTfPzKG6oiC7pKaUfPBBul2dSLNZCzViSvfzQoEvpCj6UfXsqmeB2Hv QxQNlwIYX48snUlSK6jhfAOgBAxnEN8FggLxDv4XE2vTr1yRnBdCi841ryDJPpVf 3D7ZA/hi7juOJZtvxC19dTJwAyuis0JFmKEhk6gMEFzJStUQebjtSiIMo4DB43so II+aaa6DOKocAaP4lMu3furkiGS33hfbcfJWqolxdWEy+kRGZdQ3Y/GlAyhl4NgW XijNLT6dN+rQFbeeN19Ac1cRkEoCwnveIp5CDTzfhwHCRYS+9C10vz1A9aWC1fGm ilkR74zo+dCYM0SaUch3Nw== Received: from nam11-co1-obe.outbound.protection.outlook.com (mail-co1nam11lp2175.outbound.protection.outlook.com [104.47.56.175]) by mx0a-001ae601.pphosted.com (PPS) with ESMTPS id 45kmrgvq50-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Thu, 27 Mar 2025 08:00:18 -0500 (CDT) ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=sGmmuqYjMMy6Twk8tHE0x3anUW8spQf/x8I5qVV0LCHrXGB6QgEJIEK3vQMHeDOTMNAg/liYDrN4AKXat3dz7LNuKPksubtSSXMWF7vlr84zHhFEkrVXwyedzLvYInCqB3iOIQu6HTdcJglCDPYz8s2dqa9ZDoQ1cEBsretC2jtOuDcYDHQoJY2uekV6qaoLl1DXDnYN2NjghhxYIC6SE6k4ux/dGg/CsN62P+VbWK3sSdv8P1Y8nGSYQL0GqYW9mTLb5cjZGESSUHrM79zfkHNs2T8B4PiNvxU65XWkX4YK9uNiB4TwT+BWV2OeJ+cX7pCHEZCZeC4CXYS4qjj4NA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=uaxN9Z6jdGB2UvOdNwa49OUT9bPA2k9j5KAeIDEq6yA=; b=pw3SalfmVJRp1DPknJ8h7iytYUWKMq66b6MOGA5V0T/cq2ZPgzQFN8WagpR26+9l8E+NZQZWTz0Q1NATNjmALzubStUTFCBxfGOjbBwcssIHjbEENPsny5T3XSG5oZ6U8y208ag8V/lByF91Lr8F7CkRuzLLyAhSlrAlxe02cdj6Aowbj2Lx8bYXSyfCD5RWTn9Io2ZH3YchM2rndQJTkfifXavWayVR7yaR2CcvHqGg9MtJpqPPky1cBBd1hkSHC9mmssS4AoBx6Kr81H2lWmHzPa2IOcLFDfUmj/r2zTz1NDrnC+Gjo7Fmt4XRpZHO37M8wuZdcymbSKH4kWzZ7A== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=fail (sender ip is 84.19.233.75) smtp.rcpttodomain=cirrus.com smtp.mailfrom=cirrus.com; dmarc=fail (p=reject sp=reject pct=100) action=oreject header.from=opensource.cirrus.com; dkim=none (message not signed); arc=none (0) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cirrus4.onmicrosoft.com; s=selector2-cirrus4-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=uaxN9Z6jdGB2UvOdNwa49OUT9bPA2k9j5KAeIDEq6yA=; b=h0yGYe4kq73Qt6vZUMw6IibizlJC/ZrqHHj4cFlSUfT8KD0b4Gu8rFCxiFrFt892/meqMC/UyUNpVH8YRDJ9BVo/0bKv09uwnx1XhGPwO+acZp3XqzWQ1gIsj0MzF/nI8qCsrEWBVFkL8gStaQ73/KPTcsBL6Bo8zL1Qc+I+Kzw= Received: from CH0PR07CA0020.namprd07.prod.outlook.com (2603:10b6:610:32::25) by SJ1PR19MB6236.namprd19.prod.outlook.com (2603:10b6:a03:459::12) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8534.44; Thu, 27 Mar 2025 13:00:15 +0000 Received: from CH2PEPF0000009A.namprd02.prod.outlook.com (2603:10b6:610:32:cafe::9f) by CH0PR07CA0020.outlook.office365.com (2603:10b6:610:32::25) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.20.8534.43 via Frontend Transport; Thu, 27 Mar 2025 13:00:15 +0000 X-MS-Exchange-Authentication-Results: spf=fail (sender IP is 84.19.233.75) smtp.mailfrom=cirrus.com; dkim=none (message not signed) header.d=none;dmarc=fail action=oreject header.from=opensource.cirrus.com; Received-SPF: Fail (protection.outlook.com: domain of cirrus.com does not designate 84.19.233.75 as permitted sender) receiver=protection.outlook.com; client-ip=84.19.233.75; helo=edirelay1.ad.cirrus.com; Received: from edirelay1.ad.cirrus.com (84.19.233.75) by CH2PEPF0000009A.mail.protection.outlook.com (10.167.244.22) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.20.8534.20 via Frontend Transport; Thu, 27 Mar 2025 13:00:14 +0000 Received: from ediswmail9.ad.cirrus.com (ediswmail9.ad.cirrus.com [198.61.86.93]) by edirelay1.ad.cirrus.com (Postfix) with ESMTPS id 01BF1406545; Thu, 27 Mar 2025 13:00:13 +0000 (UTC) Received: from ediswws07.ad.cirrus.com (ediswws07.ad.cirrus.com [198.90.208.14]) by ediswmail9.ad.cirrus.com (Postfix) with ESMTP id D886982255E; Thu, 27 Mar 2025 13:00:12 +0000 (UTC) From: Charles Keepax To: broonie@kernel.org Cc: lgirdwood@gmail.com, pierre-louis.bossart@linux.dev, yung-chuan.liao@linux.intel.com, peter.ujfalusi@linux.intel.com, linux-sound@vger.kernel.org, linux-kernel@vger.kernel.org, patches@opensource.cirrus.com Subject: [PATCH v2 3/3] ASoC: SDCA: Create DAI drivers from DisCo Date: Thu, 27 Mar 2025 13:00:12 +0000 Message-Id: <20250327130012.3378732-4-ckeepax@opensource.cirrus.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250327130012.3378732-1-ckeepax@opensource.cirrus.com> References: <20250327130012.3378732-1-ckeepax@opensource.cirrus.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: CH2PEPF0000009A:EE_|SJ1PR19MB6236:EE_ X-MS-Office365-Filtering-Correlation-Id: 13bcf93a-2968-405e-c38f-08dd6d2f50cd X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|36860700013|376014|82310400026|61400799027; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?ZE4mDQUL9q5dtXyZstdeJD2zbIdnW28ds9IV5m6cCBrmGMY4M1MPXWpUIpje?= =?us-ascii?Q?NfuTO8dmZXfgJjc1xv7ZPG9CfbaTYRXdp3pTCVEJ6fgJ/4jV0RwJ6tUlJZ9c?= =?us-ascii?Q?sR7dQdM5US4lrclibhMzQ+Mo1uPNzI8iUuQsFf47SGCkOmico5v9zxzxz9oA?= =?us-ascii?Q?dTE6xH6vn5xsnf93nZyOMQijMTe25WL0X0kP9gmJFAxF0Oo69Yrjd2qWVxgg?= =?us-ascii?Q?5tzYCAxhu51IRXfEiTAERP3Rj1ojrRwL1GFgpqwWdeOQ130+gWJNPgQS9IzO?= =?us-ascii?Q?7Wr6XmNcTXfb5TM42ZDCgn/zmb8WZBQhfuqMzl060tAI/33WW3t+GtvTEwcf?= =?us-ascii?Q?7zxUPxWByAQh1xTsMaBMz6/DKkT0IuOQWjon9uFLIQ+M18k72AJeh8nYWYd+?= =?us-ascii?Q?dZIk198hyo6sKIsrt/5Nq8fy/oVEP7IdM/aL9xoLc/OWYVyK+iw5PA2RZkC0?= =?us-ascii?Q?aKQ5Y596Q5CIdNLCU42ijy+8dJv7fez3n4Pm9I47LHjn94ykSPG9QnHu9Jbn?= =?us-ascii?Q?PZmWOwS7H24ajpnNDPdnQWXedkTn9jgQdAd6He4MVWCEUW5f8DZjffWD1Bw8?= =?us-ascii?Q?efkZ4g6r7FQH9iTeAt3EodaDD/o2dxD8av+5SqKssT9eDJhpKcGV8BZjjiJh?= =?us-ascii?Q?pW+jzLrCAjzHIsOfhDYFhpgydm8B2iEg4yLm+0CbBfapxKXom3VSLZRkw/2d?= =?us-ascii?Q?oUCixWlDC7bQcjjxdM0eBcLjxuJmEAXwVWqVF695vIQ27Oh3tFRATNwXbDgi?= =?us-ascii?Q?Jvfb5bVfdcxe3A3ik4jflCmoj55sOKgumaR/uSyKnkQ0sCabruxP4ztj4VFQ?= =?us-ascii?Q?hFg+JMVBnzWbE7UaBpGDW7A/XrbODnZt8iUBuJbN9oVFudbMBSKQPBbLdGE7?= =?us-ascii?Q?NcxSyOoQr7usvZXTKDVoR+xtPdGzDojFTKQuB5IkDY38cX9MdyaMC9eNj/CN?= =?us-ascii?Q?RyiEyx59hGkLmicAfqUsdYVV9ghIglRbDbuqniEWodVbimrwq1GPDRHQTuXL?= =?us-ascii?Q?zF32At1l4jqp3MlNjpzyT2Y5+k1PVxi8Nm7S3NlARTA50M29yc0Q4lofKNUe?= =?us-ascii?Q?sVl5ma49sl67FJXtHCpnubWzBFVdNim3ZPUS2a6vywoJn8gmfVsHTAjM3kTi?= =?us-ascii?Q?iiTQS6VNYsN+Tzz1HJg93c3RCjHxVcJHykTDm7TBc+cVVxPr4Sj8rB9iphHC?= =?us-ascii?Q?LKsz9JPuKtL7CC19RO9W9r3asHClxxHoMoVtogk5yg836e3RdxERhgLQtIXB?= =?us-ascii?Q?2+IL1Ni+15SwdeX7l2rymTcXG9iZt9jD9QoWDnj0Dy8MX4Y8PAPMPG6kKOfE?= =?us-ascii?Q?buGHRRGukVEXAO5Xbyyz843/1TJMdQOy8q7toG0qT+mCFv+cp67df7reCnKb?= =?us-ascii?Q?pY76QEs7cC37wuW1aVWrfI8S3y2qjHOPirNXvSf+LHQpKcd5l5U79vZ4h684?= =?us-ascii?Q?Oc94ZFMNNdbI3KeRJ0eHkrYOt6ScW5CxtuXvmlIOrLKsCeN+zxpwhQk+GcPX?= =?us-ascii?Q?nO2BXWTzx4ofrfHrGMOKRNulTXwrb8fuDAHQ?= X-Forefront-Antispam-Report: CIP:84.19.233.75;CTRY:GB;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:edirelay1.ad.cirrus.com;PTR:InfoDomainNonexistent;CAT:NONE;SFS:(13230040)(36860700013)(376014)(82310400026)(61400799027);DIR:OUT;SFP:1102; X-OriginatorOrg: opensource.cirrus.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 27 Mar 2025 13:00:14.0531 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 13bcf93a-2968-405e-c38f-08dd6d2f50cd X-MS-Exchange-CrossTenant-Id: bec09025-e5bc-40d1-a355-8e955c307de8 X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=bec09025-e5bc-40d1-a355-8e955c307de8;Ip=[84.19.233.75];Helo=[edirelay1.ad.cirrus.com] X-MS-Exchange-CrossTenant-AuthSource: CH2PEPF0000009A.namprd02.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: SJ1PR19MB6236 X-Authority-Analysis: v=2.4 cv=GrdC+l1C c=1 sm=1 tr=0 ts=67e54be2 cx=c_pps a=AuG0SFjpmAmqNFFXyzUckA==:117 a=h1hSm8JtM9GN1ddwPAif2w==:17 a=wKuvFiaSGQ0qltdbU6+NXLB8nM8=:19 a=Ol13hO9ccFRV9qXi2t6ftBPywas=:19 a=Vs1iUdzkB0EA:10 a=s63m1ICgrNkA:10 a=RWc_ulEos4gA:10 a=w1d2syhTAAAA:8 a=mtmGHodxziJHnfEkOdcA:9 a=BGLuxUZjE2igh1l4FkT-:22 X-Proofpoint-GUID: 7Lu2wQAJeb5-AITz5eyS1b0uXrvjqB09 X-Proofpoint-ORIG-GUID: 7Lu2wQAJeb5-AITz5eyS1b0uXrvjqB09 X-Proofpoint-Spam-Reason: safe Content-Type: text/plain; charset="utf-8" Use the previously parsed DisCo information from ACPI to create the DAI drivers required to connect an SDCA Function into an ASoC soundcard. Create DAI driver structures and populate the supported sample rates and sample widths into them based on the Input/Output Terminal and any attach Clock Source entities. More complex relationships with channels etc. will be added later as constraints as part of the DAI startup. Signed-off-by: Charles Keepax --- No change since v1. include/sound/sdca_asoc.h | 12 +- include/sound/sdca_function.h | 23 ++++ sound/soc/sdca/sdca_asoc.c | 209 +++++++++++++++++++++++++++++++++- 3 files changed, 238 insertions(+), 6 deletions(-) diff --git a/include/sound/sdca_asoc.h b/include/sound/sdca_asoc.h index d19e7e969283..9121531f0826 100644 --- a/include/sound/sdca_asoc.h +++ b/include/sound/sdca_asoc.h @@ -14,11 +14,14 @@ struct device; struct sdca_function_data; struct snd_kcontrol_new; struct snd_soc_component_driver; +struct snd_soc_dai_driver; +struct snd_soc_dai_ops; struct snd_soc_dapm_route; struct snd_soc_dapm_widget; =20 int sdca_asoc_count_component(struct device *dev, struct sdca_function_dat= a *function, - int *num_widgets, int *num_routes, int *num_controls); + int *num_widgets, int *num_routes, int *num_controls, + int *num_dais); =20 int sdca_asoc_populate_dapm(struct device *dev, struct sdca_function_data = *function, struct snd_soc_dapm_widget *widgets, @@ -26,9 +29,14 @@ int sdca_asoc_populate_dapm(struct device *dev, struct s= dca_function_data *funct int sdca_asoc_populate_controls(struct device *dev, struct sdca_function_data *function, struct snd_kcontrol_new *kctl); +int sdca_asoc_populate_dais(struct device *dev, struct sdca_function_data = *function, + struct snd_soc_dai_driver *dais, + const struct snd_soc_dai_ops *ops); =20 int sdca_asoc_populate_component(struct device *dev, struct sdca_function_data *function, - struct snd_soc_component_driver *component_drv); + struct snd_soc_component_driver *component_drv, + struct snd_soc_dai_driver **dai_drv, int *num_dai_drv, + const struct snd_soc_dai_ops *ops); =20 #endif // __SDCA_ASOC_H__ diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h index 77ffb1f4e1ca..be7e4a88cbed 100644 --- a/include/sound/sdca_function.h +++ b/include/sound/sdca_function.h @@ -168,6 +168,20 @@ enum sdca_ot_controls { SDCA_CTL_OT_NDAI_PACKETTYPE =3D 0x17, }; =20 +/** + * enum sdca_usage_range - Column definitions for Usage + */ +enum sdca_usage_range { + SDCA_USAGE_NUMBER =3D 0, + SDCA_USAGE_CBN =3D 1, + SDCA_USAGE_SAMPLE_RATE =3D 2, + SDCA_USAGE_SAMPLE_WIDTH =3D 3, + SDCA_USAGE_FULL_SCALE =3D 4, + SDCA_USAGE_NOISE_FLOOR =3D 5, + SDCA_USAGE_TAG =3D 6, + SDCA_USAGE_NCOLS =3D 7, +}; + /** * enum sdca_mu_controls - SDCA Controls for Mixer Unit * @@ -246,6 +260,15 @@ enum sdca_cs_controls { SDCA_CTL_CS_SAMPLERATEINDEX =3D 0x10, }; =20 +/** + * enum sdca_samplerateindex_range - Column definitions for SampleRateIndex + */ +enum sdca_samplerateindex_range { + SDCA_SAMPLERATEINDEX_INDEX =3D 0, + SDCA_SAMPLERATEINDEX_RATE =3D 1, + SDCA_SAMPLERATEINDEX_NCOLS =3D 2, +}; + /** * enum sdca_cx_controls - SDCA Controls for Clock Selector * diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index a3184189eda0..3bfeb47d2111 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -81,13 +81,15 @@ static bool exported_control(struct sdca_control *contr= ol) } =20 int sdca_asoc_count_component(struct device *dev, struct sdca_function_dat= a *function, - int *num_widgets, int *num_routes, int *num_controls) + int *num_widgets, int *num_routes, int *num_controls, + int *num_dais) { int i, j; =20 *num_widgets =3D function->num_entities - 1; *num_routes =3D 0; *num_controls =3D 0; + *num_dais =3D 0; =20 for (i =3D 0; i < function->num_entities - 1; i++) { struct sdca_entity *entity =3D &function->entities[i]; @@ -97,6 +99,7 @@ int sdca_asoc_count_component(struct device *dev, struct = sdca_function_data *fun case SDCA_ENTITY_TYPE_OT: *num_routes +=3D !!entity->iot.clock; *num_routes +=3D !!entity->iot.is_dataport; + *num_dais +=3D !!entity->iot.is_dataport; break; case SDCA_ENTITY_TYPE_PDE: *num_routes +=3D entity->pde.num_managed; @@ -942,18 +945,205 @@ int sdca_asoc_populate_controls(struct device *dev, } EXPORT_SYMBOL_NS(sdca_asoc_populate_controls, "SND_SOC_SDCA"); =20 +static unsigned int rate_find_mask(unsigned int rate) +{ + switch (rate) { + case 0: + return SNDRV_PCM_RATE_8000_768000; + case 5512: + return SNDRV_PCM_RATE_5512; + case 8000: + return SNDRV_PCM_RATE_8000; + case 11025: + return SNDRV_PCM_RATE_11025; + case 16000: + return SNDRV_PCM_RATE_16000; + case 22050: + return SNDRV_PCM_RATE_22050; + case 32000: + return SNDRV_PCM_RATE_32000; + case 44100: + return SNDRV_PCM_RATE_44100; + case 48000: + return SNDRV_PCM_RATE_48000; + case 64000: + return SNDRV_PCM_RATE_64000; + case 88200: + return SNDRV_PCM_RATE_88200; + case 96000: + return SNDRV_PCM_RATE_96000; + case 176400: + return SNDRV_PCM_RATE_176400; + case 192000: + return SNDRV_PCM_RATE_192000; + case 352800: + return SNDRV_PCM_RATE_352800; + case 384000: + return SNDRV_PCM_RATE_384000; + case 705600: + return SNDRV_PCM_RATE_705600; + case 768000: + return SNDRV_PCM_RATE_768000; + case 12000: + return SNDRV_PCM_RATE_12000; + case 24000: + return SNDRV_PCM_RATE_24000; + case 128000: + return SNDRV_PCM_RATE_128000; + default: + return 0; + } +} + +static u64 width_find_mask(unsigned int bits) +{ + switch (bits) { + case 0: + return SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE; + case 8: + return SNDRV_PCM_FMTBIT_S8; + case 16: + return SNDRV_PCM_FMTBIT_S16_LE; + case 20: + return SNDRV_PCM_FMTBIT_S20_LE; + case 24: + return SNDRV_PCM_FMTBIT_S24_LE; + case 32: + return SNDRV_PCM_FMTBIT_S32_LE; + default: + return 0; + } +} + +static int populate_rate_format(struct device *dev, + struct sdca_function_data *function, + struct sdca_entity *entity, + struct snd_soc_pcm_stream *stream) +{ + struct sdca_control_range *range; + unsigned int sample_rate, sample_width; + unsigned int clock_rates =3D 0; + unsigned int rates =3D 0; + u64 formats =3D 0; + int sel, i; + + switch (entity->type) { + case SDCA_ENTITY_TYPE_IT: + sel =3D SDCA_CTL_IT_USAGE; + break; + case SDCA_ENTITY_TYPE_OT: + sel =3D SDCA_CTL_OT_USAGE; + break; + default: + dev_err(dev, "%s: entity type has no usage control\n", + entity->label); + return -EINVAL; + } + + if (entity->iot.clock) { + range =3D selector_find_range(dev, entity->iot.clock, + SDCA_CTL_CS_SAMPLERATEINDEX, + SDCA_SAMPLERATEINDEX_NCOLS, 0); + if (!range) + return -EINVAL; + + for (i =3D 0; i < range->rows; i++) { + sample_rate =3D sdca_range(range, SDCA_SAMPLERATEINDEX_RATE, i); + clock_rates |=3D rate_find_mask(sample_rate); + } + } else { + clock_rates =3D UINT_MAX; + } + + range =3D selector_find_range(dev, entity, sel, SDCA_USAGE_NCOLS, 0); + if (!range) + return -EINVAL; + + for (i =3D 0; i < range->rows; i++) { + sample_rate =3D sdca_range(range, SDCA_USAGE_SAMPLE_RATE, i); + sample_rate =3D rate_find_mask(sample_rate); + + if (sample_rate & clock_rates) { + rates |=3D sample_rate; + + sample_width =3D sdca_range(range, SDCA_USAGE_SAMPLE_WIDTH, i); + formats |=3D width_find_mask(sample_width); + } + } + + stream->formats =3D formats; + stream->rates =3D rates; + + return 0; +} + +int sdca_asoc_populate_dais(struct device *dev, struct sdca_function_data = *function, + struct snd_soc_dai_driver *dais, + const struct snd_soc_dai_ops *ops) +{ + int i, j; + int ret; + + for (i =3D 0, j =3D 0; i < function->num_entities - 1; i++) { + struct sdca_entity *entity =3D &function->entities[i]; + struct snd_soc_pcm_stream *stream; + const char *stream_suffix; + + switch (entity->type) { + case SDCA_ENTITY_TYPE_IT: + stream =3D &dais[j].playback; + stream_suffix =3D "Playback"; + break; + case SDCA_ENTITY_TYPE_OT: + stream =3D &dais[j].capture; + stream_suffix =3D "Capture"; + break; + default: + continue; + } + + if (!entity->iot.is_dataport) + continue; + + stream->stream_name =3D devm_kasprintf(dev, GFP_KERNEL, "%s %s", + entity->label, stream_suffix); + if (!stream->stream_name) + return -ENOMEM; + /* Channels will be further limited by constraints */ + stream->channels_min =3D 1; + stream->channels_max =3D SDCA_MAX_CHANNEL_COUNT; + + ret =3D populate_rate_format(dev, function, entity, stream); + if (ret) + return ret; + + dais[j].id =3D i; + dais[j].name =3D entity->label; + dais[j].ops =3D ops; + j++; + } + + return 0; +} +EXPORT_SYMBOL_NS(sdca_asoc_populate_dais, "SND_SOC_SDCA"); + int sdca_asoc_populate_component(struct device *dev, struct sdca_function_data *function, - struct snd_soc_component_driver *component_drv) + struct snd_soc_component_driver *component_drv, + struct snd_soc_dai_driver **dai_drv, int *num_dai_drv, + const struct snd_soc_dai_ops *ops) { struct snd_soc_dapm_widget *widgets; struct snd_soc_dapm_route *routes; struct snd_kcontrol_new *controls; - int num_widgets, num_routes, num_controls; + struct snd_soc_dai_driver *dais; + int num_widgets, num_routes, num_controls, num_dais; int ret; =20 ret =3D sdca_asoc_count_component(dev, function, &num_widgets, &num_route= s, - &num_controls); + &num_controls, &num_dais); if (ret) return ret; =20 @@ -969,6 +1159,10 @@ int sdca_asoc_populate_component(struct device *dev, if (!controls) return -ENOMEM; =20 + dais =3D devm_kcalloc(dev, num_dais, sizeof(*dais), GFP_KERNEL); + if (!dais) + return -ENOMEM; + ret =3D sdca_asoc_populate_dapm(dev, function, widgets, routes); if (ret) return ret; @@ -977,6 +1171,10 @@ int sdca_asoc_populate_component(struct device *dev, if (ret) return ret; =20 + ret =3D sdca_asoc_populate_dais(dev, function, dais, ops); + if (ret) + return ret; + component_drv->dapm_widgets =3D widgets; component_drv->num_dapm_widgets =3D num_widgets; component_drv->dapm_routes =3D routes; @@ -984,6 +1182,9 @@ int sdca_asoc_populate_component(struct device *dev, component_drv->controls =3D controls; component_drv->num_controls =3D num_controls; =20 + *dai_drv =3D dais; + *num_dai_drv =3D num_dais; + return 0; } EXPORT_SYMBOL_NS(sdca_asoc_populate_component, "SND_SOC_SDCA"); --=20 2.39.5