From patchwork Mon Mar 20 20:35:16 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marian Postevca X-Patchwork-Id: 666185 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 12BEFC6FD1C for ; Mon, 20 Mar 2023 20:37:20 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id E6C8E1F3; Mon, 20 Mar 2023 21:36:28 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz E6C8E1F3 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1679344639; bh=GC5sVkvOni4mr77iGtpCS30XrOnHNfFi25Iw8L/nRxE=; h=From:To:Subject:Date:In-Reply-To:References:CC:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=u2pnM+Onfru1p0DdUdJ71TmyDARt3T9iRWBVvq99fm2mFKMO84Upi2hZw+5EbLTBm x9hLRjLRTcAlQbEBklJS8xNN8ShRi6EMXhd0IjQVOyCRaFmEeI+kiCXYEn+1OvluVZ cxYjCYVteKnNK6WyE2JCFpIQ52RoADiTb6Vbxlvk= Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id 8180DF80254; Mon, 20 Mar 2023 21:35:39 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id F34EAF804B1; Mon, 20 Mar 2023 21:35:35 +0100 (CET) Received: from mail.mutex.one (mail.mutex.one [62.77.152.124]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 3B3AAF80093 for ; Mon, 20 Mar 2023 21:35:26 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 3B3AAF80093 Authentication-Results: alsa1.perex.cz; dkim=pass (1024-bit key, unprotected) header.d=mutex.one header.i=@mutex.one header.a=rsa-sha256 header.s=default header.b=PwrkcMN/ Received: from localhost (localhost.localdomain [127.0.0.1]) by mail.mutex.one (Postfix) with ESMTP id E712C16C004E; Mon, 20 Mar 2023 22:35:25 +0200 (EET) X-Virus-Scanned: Debian amavisd-new at mail.mutex.one Received: from mail.mutex.one ([127.0.0.1]) by localhost (mail.mutex.one [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id ATKV22FdkbbL; Mon, 20 Mar 2023 22:35:24 +0200 (EET) From: Marian Postevca DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=mutex.one; s=default; t=1679344524; bh=GC5sVkvOni4mr77iGtpCS30XrOnHNfFi25Iw8L/nRxE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=PwrkcMN/5cd3mPPHPOLS53EvBAgNRnUsMLad6JYLO2a2jtOT76H6k0XfOWmKQtmwG cDfmO79n3Z/hyodiC9iIMrBxOSpNyy1rwdnkEG4w8pOhX+tPD86whjfrCVRXGvMFGZ 8yIKpVnFQWK5PI0CPZ76y/uG4B8SPaRnLUbI4lsI= To: Takashi Iwai , Mark Brown , Liam Girdwood , Jaroslav Kysela Subject: [PATCH 1/4] ASoC: es8316: Enable support for S32 LE format and MCLK div by 2 Date: Mon, 20 Mar 2023 22:35:16 +0200 Message-Id: <20230320203519.20137-2-posteuca@mutex.one> In-Reply-To: <20230320203519.20137-1-posteuca@mutex.one> References: <20230320203519.20137-1-posteuca@mutex.one> MIME-Version: 1.0 Message-ID-Hash: WDBMYROMJE3VDAYMHYK2LMYZOGX6DDTB X-Message-ID-Hash: WDBMYROMJE3VDAYMHYK2LMYZOGX6DDTB X-MailFrom: posteuca@mutex.one X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: linux-kernel@vger.kernel.org, alsa-devel@alsa-project.org, Marian Postevca X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: To properly support a line of Huawei laptops with AMD CPU and a ES8336 codec connected to the ACP3X module we need to enable the S32 LE format and the codec option to divide the MCLK by 2. The option to divide the MCLK will be enabled for one SKU with a 48Mhz MCLK. This frequency seems to be too high for the codec and leads to distorted sounds when the option is not enabled. Signed-off-by: Marian Postevca --- sound/soc/codecs/es8316.c | 21 +++++++++++++++++---- sound/soc/codecs/es8316.h | 3 +++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c index 056c3082fe02..acf21ef59b34 100644 --- a/sound/soc/codecs/es8316.c +++ b/sound/soc/codecs/es8316.c @@ -26,10 +26,11 @@ /* In slave mode at single speed, the codec is documented as accepting 5 * MCLK/LRCK ratios, but we also add ratio 400, which is commonly used on * Intel Cherry Trail platforms (19.2MHz MCLK, 48kHz LRCK). + * Ratio 1000 is needed for at least one SKU where MCLK is 48Mhz. */ -#define NR_SUPPORTED_MCLK_LRCK_RATIOS 6 +#define NR_SUPPORTED_MCLK_LRCK_RATIOS 7 static const unsigned int supported_mclk_lrck_ratios[] = { - 256, 384, 400, 512, 768, 1024 + 256, 384, 400, 512, 768, 1000, 1024 }; struct es8316_priv { @@ -465,6 +466,8 @@ static int es8316_pcm_hw_params(struct snd_pcm_substream *substream, u8 bclk_divider; u16 lrck_divider; int i; + bool mclk_div_option = false; + unsigned int mclk_div = 1; /* Validate supported sample rates that are autodetected from MCLK */ for (i = 0; i < NR_SUPPORTED_MCLK_LRCK_RATIOS; i++) { @@ -477,7 +480,17 @@ static int es8316_pcm_hw_params(struct snd_pcm_substream *substream, } if (i == NR_SUPPORTED_MCLK_LRCK_RATIOS) return -EINVAL; - lrck_divider = es8316->sysclk / params_rate(params); + + mclk_div_option = device_property_read_bool(component->dev, + "everest,mclk-div-by-2"); + if (mclk_div_option) { + snd_soc_component_update_bits(component, ES8316_CLKMGR_CLKSW, + ES8316_CLKMGR_CLKSW_MCLK_DIV, + ES8316_CLKMGR_CLKSW_MCLK_DIV); + mclk_div = 2; + } + + lrck_divider = es8316->sysclk / params_rate(params) / mclk_div; bclk_divider = lrck_divider / 4; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: @@ -520,7 +533,7 @@ static int es8316_mute(struct snd_soc_dai *dai, int mute, int direction) } #define ES8316_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ - SNDRV_PCM_FMTBIT_S24_LE) + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) static const struct snd_soc_dai_ops es8316_ops = { .startup = es8316_pcm_startup, diff --git a/sound/soc/codecs/es8316.h b/sound/soc/codecs/es8316.h index c335138e2837..0ff16f948690 100644 --- a/sound/soc/codecs/es8316.h +++ b/sound/soc/codecs/es8316.h @@ -129,4 +129,7 @@ #define ES8316_GPIO_FLAG_GM_NOT_SHORTED 0x02 #define ES8316_GPIO_FLAG_HP_NOT_INSERTED 0x04 +/* ES8316_CLKMGR_CLKSW */ +#define ES8316_CLKMGR_CLKSW_MCLK_DIV 0x80 + #endif From patchwork Mon Mar 20 20:35:17 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marian Postevca X-Patchwork-Id: 665201 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 26C9BC6FD1D for ; Mon, 20 Mar 2023 20:37:39 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 0CE0C1CF; Mon, 20 Mar 2023 21:36:47 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 0CE0C1CF DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1679344657; bh=InYQgdpge/dbR4Re0sZSGzLb+2FQvnDHCp4OaMBC+ys=; h=From:To:Subject:Date:In-Reply-To:References:CC:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=nguzHkeiGD7ZgpNtksdqJxfyoMJKD05fU6zsfqs8GpnF8Cp1XqGLTp47wUlB0isxM /F58TmKqVLY0TU/Dibao+n/6wc44rPcbL239Uvk+jmTdl2TlvD2zNKjjBeapc2D1d7 vfH9tm98eqbCIXWhGglcEa17AaEGlA1GGC4M4Rjg= Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id 1C52AF80529; Mon, 20 Mar 2023 21:35:43 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 6CB64F80520; Mon, 20 Mar 2023 21:35:38 +0100 (CET) Received: from mail.mutex.one (mail.mutex.one [62.77.152.124]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 86F4BF80254 for ; Mon, 20 Mar 2023 21:35:27 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 86F4BF80254 Authentication-Results: alsa1.perex.cz; dkim=pass (1024-bit key, unprotected) header.d=mutex.one header.i=@mutex.one header.a=rsa-sha256 header.s=default header.b=QOc1/SZG Received: from localhost (localhost.localdomain [127.0.0.1]) by mail.mutex.one (Postfix) with ESMTP id 2350C16C004F; Mon, 20 Mar 2023 22:35:27 +0200 (EET) X-Virus-Scanned: Debian amavisd-new at mail.mutex.one Received: from mail.mutex.one ([127.0.0.1]) by localhost (mail.mutex.one [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id EtRulhLEt9cx; Mon, 20 Mar 2023 22:35:25 +0200 (EET) From: Marian Postevca DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=mutex.one; s=default; t=1679344525; bh=InYQgdpge/dbR4Re0sZSGzLb+2FQvnDHCp4OaMBC+ys=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=QOc1/SZGenka+FTPOx96+ywGP8ItKBLxq0rLHiGeCxo/MNXZYrO5XoIfrb4TM7Nqm gw0HRsZ19InUSBTsyn/ayHnq1oFewCpU5z2UjjIHonx69+4UFp5Ca5OXgwH6MvwJCv /YlbivPQe5CdrpzGteWoHkgEWtRhz4uJyl3ySmNo= To: Takashi Iwai , Mark Brown , Liam Girdwood , Jaroslav Kysela Subject: [PATCH 2/4] ASoC: amd: acp: Add support for splitting the codec specific code from the ACP driver Date: Mon, 20 Mar 2023 22:35:17 +0200 Message-Id: <20230320203519.20137-3-posteuca@mutex.one> In-Reply-To: <20230320203519.20137-1-posteuca@mutex.one> References: <20230320203519.20137-1-posteuca@mutex.one> MIME-Version: 1.0 Message-ID-Hash: 34Q3YMLRQHLK4XBEOKS7PNMZSOZWBG3T X-Message-ID-Hash: 34Q3YMLRQHLK4XBEOKS7PNMZSOZWBG3T X-MailFrom: posteuca@mutex.one X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: linux-kernel@vger.kernel.org, alsa-devel@alsa-project.org, Marian Postevca X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: This commit adds support for splitting more complicated machine drivers, that need special handling, from the generic ACP code. By adding support for callbacks to configure and handle codec specific implementation details, we can split them in separate files that don't clutter the ACP code. Signed-off-by: Marian Postevca --- sound/soc/amd/acp/acp-mach.h | 65 ++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/sound/soc/amd/acp/acp-mach.h b/sound/soc/amd/acp/acp-mach.h index 165f407697c0..2cade68e6cc3 100644 --- a/sound/soc/amd/acp/acp-mach.h +++ b/sound/soc/amd/acp/acp-mach.h @@ -20,6 +20,10 @@ #define TDM_CHANNELS 8 +#define ACP_OPS(priv, cb) ((priv)->ops.cb) + +#define acp_get_drvdata(card) ((struct acp_card_drvdata *)(card)->drvdata) + enum be_id { HEADSET_BE_ID = 0, AMP_BE_ID, @@ -48,6 +52,14 @@ enum platform_end_point { REMBRANDT, }; +struct acp_mach_ops { + int (*probe)(struct snd_soc_card *card); + int (*configure_link)(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); + int (*configure_widgets)(struct snd_soc_card *card); + int (*suspend_pre)(struct snd_soc_card *card); + int (*resume_post)(struct snd_soc_card *card); +}; + struct acp_card_drvdata { unsigned int hs_cpu_id; unsigned int amp_cpu_id; @@ -59,6 +71,8 @@ struct acp_card_drvdata { unsigned int platform; struct clk *wclk; struct clk *bclk; + struct acp_mach_ops ops; + void *mach_priv; bool soc_mclk; bool tdm_mode; }; @@ -67,4 +81,55 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card); int acp_legacy_dai_links_create(struct snd_soc_card *card); extern const struct dmi_system_id acp_quirk_table[]; +static inline int acp_ops_probe(struct snd_soc_card *card) +{ + int ret = 1; + struct acp_card_drvdata *priv = acp_get_drvdata(card); + + if (ACP_OPS(priv, probe)) + ret = ACP_OPS(priv, probe)(card); + return ret; +} + +static inline int acp_ops_configure_link(struct snd_soc_card *card, + struct snd_soc_dai_link *dai_link) +{ + int ret = 1; + struct acp_card_drvdata *priv = acp_get_drvdata(card); + + if (ACP_OPS(priv, configure_link)) + ret = ACP_OPS(priv, configure_link)(card, dai_link); + return ret; +} + +static inline int acp_ops_configure_widgets(struct snd_soc_card *card) +{ + int ret = 1; + struct acp_card_drvdata *priv = acp_get_drvdata(card); + + if (ACP_OPS(priv, configure_widgets)) + ret = ACP_OPS(priv, configure_widgets)(card); + return ret; +} + +static inline int acp_ops_suspend_pre(struct snd_soc_card *card) +{ + int ret = 1; + struct acp_card_drvdata *priv = acp_get_drvdata(card); + + if (ACP_OPS(priv, suspend_pre)) + ret = ACP_OPS(priv, suspend_pre)(card); + return ret; +} + +static inline int acp_ops_resume_post(struct snd_soc_card *card) +{ + int ret = 1; + struct acp_card_drvdata *priv = acp_get_drvdata(card); + + if (ACP_OPS(priv, resume_post)) + ret = ACP_OPS(priv, resume_post)(card); + return ret; +} + #endif From patchwork Mon Mar 20 20:35:18 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marian Postevca X-Patchwork-Id: 665200 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id D65B5C6FD1C for ; Mon, 20 Mar 2023 20:38:08 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id D6675210; Mon, 20 Mar 2023 21:37:16 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz D6675210 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1679344686; bh=3K+HfyPQjIbBIoDLQUwDUdraZ5M4IxrG2vAebklpJHk=; h=From:To:Subject:Date:In-Reply-To:References:CC:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=D6no5jNwv6SisWuiAxkrQfAHnwEC9pYWDJiauT9Um8FqfXQM21YBf+1fEZ/yvKXhA jBRimu2X+z9luIttI9uTSPNNq49TiszgwEZU6IjP+/ZuNk4PH654yUPN/tOpspvqIj 9qAXLFUisvabRLkt/LCsugPq93y3gRXWGUXKLJdw= Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id D2633F80549; Mon, 20 Mar 2023 21:36:25 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id CDDE5F80549; Mon, 20 Mar 2023 21:36:21 +0100 (CET) Received: from mail.mutex.one (mail.mutex.one [62.77.152.124]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 0C0E6F8027B for ; Mon, 20 Mar 2023 21:35:30 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 0C0E6F8027B Authentication-Results: alsa1.perex.cz; dkim=pass (1024-bit key, unprotected) header.d=mutex.one header.i=@mutex.one header.a=rsa-sha256 header.s=default header.b=mVDuEfLn Received: from localhost (localhost.localdomain [127.0.0.1]) by mail.mutex.one (Postfix) with ESMTP id 69F2D16C0055; Mon, 20 Mar 2023 22:35:29 +0200 (EET) X-Virus-Scanned: Debian amavisd-new at mail.mutex.one Received: from mail.mutex.one ([127.0.0.1]) by localhost (mail.mutex.one [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id YBJ1tyd7JdxF; Mon, 20 Mar 2023 22:35:27 +0200 (EET) From: Marian Postevca DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=mutex.one; s=default; t=1679344527; bh=3K+HfyPQjIbBIoDLQUwDUdraZ5M4IxrG2vAebklpJHk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=mVDuEfLnU4h7PO9rqdJpGKlDnttj41EDc3enI4A9o6a6GoV/PJOnaMUz5g2HVj/RT m52HVBh6yGhfLUgkD4E1j4ryz2+W9pW1fiNLJW1Mmjj5aKOpcFYMSHcl2xahE797x+ Ri38U5ziZHhGGry+yufasnCQnMwTCYwEugl0+MXM= To: Takashi Iwai , Mark Brown , Liam Girdwood , Jaroslav Kysela Subject: [PATCH 3/4] ASoC: amd: acp: Add machine driver that enables sound for systems with a ES8336 codec Date: Mon, 20 Mar 2023 22:35:18 +0200 Message-Id: <20230320203519.20137-4-posteuca@mutex.one> In-Reply-To: <20230320203519.20137-1-posteuca@mutex.one> References: <20230320203519.20137-1-posteuca@mutex.one> MIME-Version: 1.0 Message-ID-Hash: O3ZWXYPRD2MBVEJWTQXVK6CUMD3CZZQQ X-Message-ID-Hash: O3ZWXYPRD2MBVEJWTQXVK6CUMD3CZZQQ X-MailFrom: posteuca@mutex.one X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: linux-kernel@vger.kernel.org, alsa-devel@alsa-project.org, Marian Postevca X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: This commit enables sound for a line of Huawei laptops that use the ES8336 codec which is connected to the ACP3X module. Signed-off-by: Marian Postevca --- sound/soc/amd/acp-config.c | 70 +++ sound/soc/amd/acp/Makefile | 2 +- sound/soc/amd/acp/acp-legacy-mach.c | 105 +++- sound/soc/amd/acp/acp-mach-common.c | 8 + sound/soc/amd/acp/acp-mach.h | 2 + sound/soc/amd/acp/acp-renoir.c | 4 + sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c | 471 ++++++++++++++++++ sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.h | 12 + 8 files changed, 657 insertions(+), 17 deletions(-) create mode 100644 sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c create mode 100644 sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.h diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c index 0932473b6394..76e65c0ad140 100644 --- a/sound/soc/amd/acp-config.c +++ b/sound/soc/amd/acp-config.c @@ -47,6 +47,76 @@ static const struct config_entry config_table[] = { {} }, }, + { + .flags = FLAG_AMD_LEGACY, + .device = ACP_PCI_DEV_ID, + .dmi_table = (const struct dmi_system_id []) { + { + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXXW"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"), + }, + }, + {} + }, + }, + { + .flags = FLAG_AMD_LEGACY, + .device = ACP_PCI_DEV_ID, + .dmi_table = (const struct dmi_system_id []) { + { + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXX9"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"), + }, + }, + {} + }, + }, + { + .flags = FLAG_AMD_LEGACY, + .device = ACP_PCI_DEV_ID, + .dmi_table = (const struct dmi_system_id []) { + { + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "BOM-WXX9"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"), + }, + }, + {} + }, + }, + { + .flags = FLAG_AMD_LEGACY, + .device = ACP_PCI_DEV_ID, + .dmi_table = (const struct dmi_system_id []) { + { + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1020"), + }, + }, + {} + }, + }, + { + .flags = FLAG_AMD_LEGACY, + .device = ACP_PCI_DEV_ID, + .dmi_table = (const struct dmi_system_id []) { + { + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1040"), + }, + }, + {} + }, + }, }; int snd_amd_acp_find_config(struct pci_dev *pci) diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile index d9abb0ee5218..1bf64a9289a2 100644 --- a/sound/soc/amd/acp/Makefile +++ b/sound/soc/amd/acp/Makefile @@ -16,7 +16,7 @@ snd-acp-rembrandt-objs := acp-rembrandt.o #machine specific driver snd-acp-mach-objs := acp-mach-common.o -snd-acp-legacy-mach-objs := acp-legacy-mach.o +snd-acp-legacy-mach-objs := acp-legacy-mach.o acp3x-es83xx/acp3x-es83xx.o snd-acp-sof-mach-objs := acp-sof-mach.o obj-$(CONFIG_SND_SOC_AMD_ACP_PCM) += snd-acp-pcm.o diff --git a/sound/soc/amd/acp/acp-legacy-mach.c b/sound/soc/amd/acp/acp-legacy-mach.c index 676ad50638d0..8ecda4780d4a 100644 --- a/sound/soc/amd/acp/acp-legacy-mach.c +++ b/sound/soc/amd/acp/acp-legacy-mach.c @@ -20,6 +20,7 @@ #include #include "acp-mach.h" +#include "acp3x-es83xx/acp3x-es83xx.h" static struct acp_card_drvdata rt5682_rt1019_data = { .hs_cpu_id = I2S_SP, @@ -51,6 +52,14 @@ static struct acp_card_drvdata rt5682s_rt1019_data = { .tdm_mode = false, }; +static struct acp_card_drvdata es83xx_rn_data = { + .hs_cpu_id = I2S_SP, + .dmic_cpu_id = DMIC, + .hs_codec_id = ES83XX, + .dmic_codec_id = DMIC, + .platform = RENOIR, +}; + static struct acp_card_drvdata max_nau8825_data = { .hs_cpu_id = I2S_HS, .amp_cpu_id = I2S_HS, @@ -92,6 +101,33 @@ static const struct snd_soc_dapm_widget acp_widgets[] = { SND_SOC_DAPM_SPK("Right Spk", NULL), }; +static bool acp_asoc_init_ops(struct acp_card_drvdata *priv) +{ + bool has_ops = false; + + if (priv->hs_codec_id == ES83XX) { + has_ops = true; + acp3x_es83xx_init_ops(&priv->ops); + } + return has_ops; +} + +static int acp_asoc_suspend_pre(struct snd_soc_card *card) +{ + int ret; + + ret = acp_ops_suspend_pre(card); + return ret == 1 ? 0 : ret; +} + +static int acp_asoc_resume_post(struct snd_soc_card *card) +{ + int ret; + + ret = acp_ops_resume_post(card); + return ret == 1 ? 0 : ret; +} + static int acp_asoc_probe(struct platform_device *pdev) { struct snd_soc_card *card = NULL; @@ -100,38 +136,70 @@ static int acp_asoc_probe(struct platform_device *pdev) struct acp_card_drvdata *acp_card_drvdata; int ret; - if (!pdev->id_entry) - return -EINVAL; + if (!pdev->id_entry) { + ret = -EINVAL; + goto out; + } card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); - if (!card) - return -ENOMEM; + if (!card) { + ret = -ENOMEM; + goto out; + } + card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data; + acp_card_drvdata = card->drvdata; + acp_card_drvdata->acpi_mach = (struct snd_soc_acpi_mach *)pdev->dev.platform_data; card->dev = dev; card->owner = THIS_MODULE; card->name = pdev->id_entry->name; - card->dapm_widgets = acp_widgets; - card->num_dapm_widgets = ARRAY_SIZE(acp_widgets); - card->controls = acp_controls; - card->num_controls = ARRAY_SIZE(acp_controls); - card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data; - acp_card_drvdata = card->drvdata; + acp_asoc_init_ops(card->drvdata); + + ret = acp_ops_configure_widgets(card); + if (ret == 1) { + card->dapm_widgets = acp_widgets; + card->num_dapm_widgets = ARRAY_SIZE(acp_widgets); + card->controls = acp_controls; + card->num_controls = ARRAY_SIZE(acp_controls); + } else if (ret < 0) { + dev_err(&pdev->dev, + "Cannot configure widgets for card (%s): %d\n", + card->name, ret); + goto out; + } + card->suspend_pre = acp_asoc_suspend_pre; + card->resume_post = acp_asoc_resume_post; + + ret = acp_ops_probe(card); + if (ret < 0) { + dev_err(&pdev->dev, + "Cannot probe card (%s): %d\n", + card->name, ret); + goto out; + } + dmi_id = dmi_first_match(acp_quirk_table); if (dmi_id && dmi_id->driver_data) acp_card_drvdata->tdm_mode = dmi_id->driver_data; - acp_legacy_dai_links_create(card); + ret = acp_legacy_dai_links_create(card); + if (ret) { + dev_err(&pdev->dev, + "Cannot create dai links for card (%s): %d\n", + card->name, ret); + goto out; + } ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, - "devm_snd_soc_register_card(%s) failed: %d\n", - card->name, ret); - return ret; + "devm_snd_soc_register_card(%s) failed: %d\n", + card->name, ret); + goto out; } - - return 0; +out: + return ret; } static const struct platform_device_id board_ids[] = { @@ -147,6 +215,10 @@ static const struct platform_device_id board_ids[] = { .name = "acp3xalc5682s1019", .driver_data = (kernel_ulong_t)&rt5682s_rt1019_data, }, + { + .name = "acp3x-es83xx", + .driver_data = (kernel_ulong_t)&es83xx_rn_data, + }, { .name = "rmb-nau8825-max", .driver_data = (kernel_ulong_t)&max_nau8825_data, @@ -173,6 +245,7 @@ MODULE_DESCRIPTION("ACP chrome audio support"); MODULE_ALIAS("platform:acp3xalc56821019"); MODULE_ALIAS("platform:acp3xalc5682sm98360"); MODULE_ALIAS("platform:acp3xalc5682s1019"); +MODULE_ALIAS("platform:acp3x-es83xx"); MODULE_ALIAS("platform:rmb-nau8825-max"); MODULE_ALIAS("platform:rmb-rt5682s-rt1019"); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index b4dcce4fbae9..16dfebfbb4b6 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -1053,6 +1053,7 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card) struct device *dev = card->dev; struct acp_card_drvdata *drv_data = card->drvdata; int i = 0, num_links = 0; + int rc; if (drv_data->hs_cpu_id) num_links++; @@ -1091,6 +1092,13 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card) links[i].init = acp_card_rt5682s_init; links[i].ops = &acp_card_rt5682s_ops; } + if (drv_data->hs_codec_id == ES83XX) { + rc = acp_ops_configure_link(card, &links[i]); + if (rc != 0) { + dev_err(dev, "Failed to configure link for ES83XX: %d\n", rc); + return rc; + } + } i++; } diff --git a/sound/soc/amd/acp/acp-mach.h b/sound/soc/amd/acp/acp-mach.h index 2cade68e6cc3..bb93c8b2dbfa 100644 --- a/sound/soc/amd/acp/acp-mach.h +++ b/sound/soc/amd/acp/acp-mach.h @@ -45,6 +45,7 @@ enum codec_endpoints { MAX98360A, RT5682S, NAU8825, + ES83XX, }; enum platform_end_point { @@ -72,6 +73,7 @@ struct acp_card_drvdata { struct clk *wclk; struct clk *bclk; struct acp_mach_ops ops; + struct snd_soc_acpi_mach *acpi_mach; void *mach_priv; bool soc_mclk; bool tdm_mode; diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c index 2a89a0d2e601..1f04e08e15d4 100644 --- a/sound/soc/amd/acp/acp-renoir.c +++ b/sound/soc/amd/acp/acp-renoir.c @@ -83,6 +83,10 @@ static struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines[] = { .id = "AMDI1019", .drv_name = "renoir-acp", }, + { + .id = "ESSX8336", + .drv_name = "acp3x-es83xx", + }, {}, }; diff --git a/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c new file mode 100644 index 000000000000..138a7c12669b --- /dev/null +++ b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c @@ -0,0 +1,471 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Machine driver for AMD ACP Audio engine using ES8336 codec. +// +// Copyright 2023 Marian Postevca + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../acp-mach.h" + +#define get_mach_priv(card) ((struct acp3x_es83xx_private *)((acp_get_drvdata(card))->mach_priv)) + +/* mclk-div-by-2 + terminating entry */ +#define MAX_NO_PROPS 2 + +#define DUAL_CHANNEL 2 + +#define ES83XX_ENABLE_DMIC BIT(4) +#define ES83XX_48_MHZ_MCLK BIT(5) + +struct acp3x_es83xx_private { + unsigned long quirk; + struct snd_soc_component *codec; + struct device *codec_dev; + struct gpio_desc *gpio_speakers, *gpio_headphone; + struct acpi_gpio_params enable_spk_gpio, enable_hp_gpio; + struct acpi_gpio_mapping gpio_mapping[3]; + struct snd_soc_dapm_route mic_map[2]; +}; + +static const unsigned int rates[] = { + 8000, 11025, 16000, 22050, 32000, + 44100, 48000, 64000, 88200, 96000, +}; + +static const unsigned int rates_48mhz_mclk[] = { + 48000, 96000, +}; + +static const unsigned int channels[] = { + DUAL_CHANNEL, +}; + +static const struct snd_pcm_hw_constraint_list hw_constraint_rates_normal = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static const struct snd_pcm_hw_constraint_list hw_constraint_rates_48mhz = { + .count = ARRAY_SIZE(rates_48mhz_mclk), + .list = rates_48mhz_mclk, + .mask = 0, +}; + +static const struct snd_pcm_hw_constraint_list constraints_channels = { + .count = ARRAY_SIZE(channels), + .list = channels, + .mask = 0, +}; + +#define ES83xx_12288_KHZ_MCLK_FREQ (48000 * 256) +#define ES83xx_48_MHZ_MCLK_FREQ (48000 * 1000) + +static int acp3x_es83xx_speaker_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); + +static void acp3x_es83xx_set_gpios_values(struct acp3x_es83xx_private *priv, + bool speaker, bool headphone) +{ + gpiod_set_value_cansleep(priv->gpio_speakers, speaker); + gpiod_set_value_cansleep(priv->gpio_headphone, headphone); +} + +static int acp3x_es83xx_create_swnode(struct device *codec_dev) +{ + struct property_entry props[MAX_NO_PROPS] = {}; + struct fwnode_handle *fwnode; + int ret; + + props[0] = PROPERTY_ENTRY_BOOL("everest,mclk-div-by-2"); + + fwnode = fwnode_create_software_node(props, NULL); + if (IS_ERR(fwnode)) + return PTR_ERR(fwnode); + + ret = device_add_software_node(codec_dev, to_software_node(fwnode)); + + fwnode_handle_put(fwnode); + return ret; +} + +static int acp3x_es83xx_codec_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai *codec_dai; + struct acp3x_es83xx_private *priv; + unsigned int freq; + int ret; + + runtime = substream->runtime; + rtd = asoc_substream_to_rtd(substream); + codec_dai = asoc_rtd_to_codec(rtd, 0); + priv = get_mach_priv(rtd->card); + + if (priv->quirk & ES83XX_48_MHZ_MCLK) { + dev_dbg(priv->codec_dev, "using a 48Mhz MCLK\n"); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_constraint_rates_48mhz); + freq = ES83xx_48_MHZ_MCLK_FREQ; + } else { + dev_dbg(priv->codec_dev, "using a 12.288Mhz MCLK\n"); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_constraint_rates_normal); + freq = ES83xx_12288_KHZ_MCLK_FREQ; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, 0, freq, SND_SOC_CLOCK_OUT); + if (ret < 0) { + dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBP_CFP); + if (ret < 0) { + dev_err(rtd->dev, "failed to set DAI fmt: %d\n", ret); + return ret; + } + + runtime->hw.channels_max = DUAL_CHANNEL; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE; + + return 0; +} + +static struct snd_soc_jack es83xx_jack; + +static struct snd_soc_jack_pin es83xx_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static const struct snd_soc_dapm_widget acp3x_es83xx_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Internal Mic", NULL), + + SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0, + acp3x_es83xx_speaker_power_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +}; + +static const struct snd_soc_dapm_route acp3x_es83xx_audio_map[] = { + {"Headphone", NULL, "HPOL"}, + {"Headphone", NULL, "HPOR"}, + + /* + * There is no separate speaker output instead the speakers are muxed to + * the HP outputs. The mux is controlled Speaker and/or headphone switch. + */ + {"Speaker", NULL, "HPOL"}, + {"Speaker", NULL, "HPOR"}, + {"Speaker", NULL, "Speaker Power"}, +}; + + +static const struct snd_kcontrol_new acp3x_es83xx_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Internal Mic"), +}; + +static int acp3x_es83xx_configure_widgets(struct snd_soc_card *card) +{ + card->dapm_widgets = acp3x_es83xx_widgets; + card->num_dapm_widgets = ARRAY_SIZE(acp3x_es83xx_widgets); + card->controls = acp3x_es83xx_controls; + card->num_controls = ARRAY_SIZE(acp3x_es83xx_controls); + card->dapm_routes = acp3x_es83xx_audio_map; + card->num_dapm_routes = ARRAY_SIZE(acp3x_es83xx_audio_map); + + return 0; +} + +static int acp3x_es83xx_speaker_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct acp3x_es83xx_private *priv = get_mach_priv(w->dapm->card); + + dev_dbg(priv->codec_dev, "speaker power event: %d\n", event); + if (SND_SOC_DAPM_EVENT_ON(event)) + acp3x_es83xx_set_gpios_values(priv, 1, 0); + else + acp3x_es83xx_set_gpios_values(priv, 0, 1); + + return 0; +} + +static int acp3x_es83xx_suspend_pre(struct snd_soc_card *card) +{ + struct acp3x_es83xx_private *priv = get_mach_priv(card); + + dev_dbg(priv->codec_dev, "card suspend\n"); + snd_soc_component_set_jack(priv->codec, NULL, NULL); + return 0; +} + +static int acp3x_es83xx_resume_post(struct snd_soc_card *card) +{ + struct acp3x_es83xx_private *priv = get_mach_priv(card); + + dev_dbg(priv->codec_dev, "card resume\n"); + snd_soc_component_set_jack(priv->codec, &es83xx_jack, NULL); + return 0; +} + +static int acp3x_es83xx_configure_gpios(struct acp3x_es83xx_private *priv) +{ + int ret = 0; + + priv->enable_spk_gpio.crs_entry_index = 0; + priv->enable_hp_gpio.crs_entry_index = 1; + + priv->enable_spk_gpio.active_low = false; + priv->enable_hp_gpio.active_low = false; + + priv->gpio_mapping[0].name = "speakers-enable-gpios"; + priv->gpio_mapping[0].data = &priv->enable_spk_gpio; + priv->gpio_mapping[0].size = 1; + priv->gpio_mapping[0].quirks = ACPI_GPIO_QUIRK_ONLY_GPIOIO; + + priv->gpio_mapping[1].name = "headphone-enable-gpios"; + priv->gpio_mapping[1].data = &priv->enable_hp_gpio; + priv->gpio_mapping[1].size = 1; + priv->gpio_mapping[1].quirks = ACPI_GPIO_QUIRK_ONLY_GPIOIO; + + dev_info(priv->codec_dev, "speaker gpio %d active %s, headphone gpio %d active %s\n", + priv->enable_spk_gpio.crs_entry_index, + priv->enable_spk_gpio.active_low ? "low" : "high", + priv->enable_hp_gpio.crs_entry_index, + priv->enable_hp_gpio.active_low ? "low" : "high"); + return ret; +} + +static int acp3x_es83xx_configure_mics(struct acp3x_es83xx_private *priv) +{ + int num_routes = 0; + int i; + + if (!(priv->quirk & ES83XX_ENABLE_DMIC)) { + priv->mic_map[num_routes].sink = "MIC1"; + priv->mic_map[num_routes].source = "Internal Mic"; + num_routes++; + } + + priv->mic_map[num_routes].sink = "MIC2"; + priv->mic_map[num_routes].source = "Headset Mic"; + num_routes++; + + for (i = 0; i < num_routes; i++) + dev_info(priv->codec_dev, "%s is %s\n", + priv->mic_map[i].source, priv->mic_map[i].sink); + + return num_routes; +} + +static int acp3x_es83xx_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_component *codec = asoc_rtd_to_codec(runtime, 0)->component; + struct snd_soc_card *card = runtime->card; + struct acp3x_es83xx_private *priv = get_mach_priv(card); + int ret = 0; + int num_routes; + + ret = snd_soc_card_jack_new_pins(card, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0, + &es83xx_jack, es83xx_jack_pins, + ARRAY_SIZE(es83xx_jack_pins)); + if (ret) { + dev_err(card->dev, "jack creation failed %d\n", ret); + return ret; + } + + snd_jack_set_key(es83xx_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + + snd_soc_component_set_jack(codec, &es83xx_jack, NULL); + + priv->codec = codec; + acp3x_es83xx_configure_gpios(priv); + + ret = devm_acpi_dev_add_driver_gpios(priv->codec_dev, priv->gpio_mapping); + if (ret) + dev_warn(priv->codec_dev, "failed to add speaker gpio\n"); + + priv->gpio_speakers = gpiod_get_optional(priv->codec_dev, "speakers-enable", + priv->enable_spk_gpio.active_low ? GPIOD_OUT_LOW : GPIOD_OUT_HIGH); + if (IS_ERR(priv->gpio_speakers)) { + dev_err(priv->codec_dev, "could not get speakers-enable GPIO\n"); + return PTR_ERR(priv->gpio_speakers); + } + + priv->gpio_headphone = gpiod_get_optional(priv->codec_dev, "headphone-enable", + priv->enable_hp_gpio.active_low ? GPIOD_OUT_LOW : GPIOD_OUT_HIGH); + if (IS_ERR(priv->gpio_headphone)) { + dev_err(priv->codec_dev, "could not get headphone-enable GPIO\n"); + return PTR_ERR(priv->gpio_headphone); + } + + if (priv->quirk & ES83XX_48_MHZ_MCLK) { + ret = acp3x_es83xx_create_swnode(priv->codec_dev); + if (ret != 0) { + dev_err(priv->codec_dev, + "could not create software node inside codec: %d\n", ret); + return ret; + } + } + + num_routes = acp3x_es83xx_configure_mics(priv); + if (num_routes > 0) { + ret = snd_soc_dapm_add_routes(&card->dapm, priv->mic_map, num_routes); + if (ret != 0) + device_remove_software_node(priv->codec_dev); + } + + return ret; +} + +static const struct snd_soc_ops acp3x_es83xx_ops = { + .startup = acp3x_es83xx_codec_startup, +}; + + +SND_SOC_DAILINK_DEF(codec, + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ESSX8336:00", "ES8316 HiFi"))); + +static const struct dmi_system_id acp3x_es83xx_dmi_table[] = { + { + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXXW"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"), + }, + .driver_data = (void *)(ES83XX_ENABLE_DMIC), + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXX9"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"), + }, + .driver_data = (void *)(ES83XX_ENABLE_DMIC), + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "BOM-WXX9"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"), + }, + .driver_data = (void *)(ES83XX_ENABLE_DMIC|ES83XX_48_MHZ_MCLK), + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1020"), + }, + .driver_data = (void *)(ES83XX_ENABLE_DMIC), + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1040"), + }, + .driver_data = (void *)(ES83XX_ENABLE_DMIC), + }, + {} +}; + +static int acp3x_es83xx_configure_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + link->codecs = codec; + link->num_codecs = ARRAY_SIZE(codec); + link->init = acp3x_es83xx_init; + link->ops = &acp3x_es83xx_ops; + + return 0; +} + +static int acp3x_es83xx_probe(struct snd_soc_card *card) +{ + int ret = 0; + struct device *dev = card->dev; + const struct dmi_system_id *dmi_id; + + dmi_id = dmi_first_match(acp3x_es83xx_dmi_table); + if (dmi_id && dmi_id->driver_data) { + struct acp3x_es83xx_private *priv; + struct acp_card_drvdata *acp_drvdata; + struct acpi_device *adev; + struct device *codec_dev; + + acp_drvdata = (struct acp_card_drvdata *)card->drvdata; + + dev_info(dev, "matched DMI table with this system, trying to register sound card\n"); + + adev = acpi_dev_get_first_match_dev(acp_drvdata->acpi_mach->id, NULL, -1); + if (!adev) { + dev_err(dev, "Error cannot find '%s' dev\n", acp_drvdata->acpi_mach->id); + return -ENXIO; + } + + codec_dev = acpi_get_first_physical_node(adev); + acpi_dev_put(adev); + if (!codec_dev) { + dev_warn(dev, "Error cannot find codec device, will defer probe\n"); + return -EPROBE_DEFER; + } + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + put_device(codec_dev); + return -ENOMEM; + } + + priv->codec_dev = codec_dev; + priv->quirk = (unsigned long)dmi_id->driver_data; + acp_drvdata->mach_priv = priv; + dev_info(dev, "successfully probed the sound card\n"); + } else { + ret = -ENODEV; + dev_warn(dev, "this system has a ES83xx codec defined in ACPI, but the driver doesn't have this system registered in DMI table\n"); + } + return ret; +} + + +void acp3x_es83xx_init_ops(struct acp_mach_ops *ops) +{ + ops->probe = acp3x_es83xx_probe; + ops->configure_widgets = acp3x_es83xx_configure_widgets; + ops->configure_link = acp3x_es83xx_configure_link; + ops->suspend_pre = acp3x_es83xx_suspend_pre; + ops->resume_post = acp3x_es83xx_resume_post; +} diff --git a/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.h b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.h new file mode 100644 index 000000000000..03551ffdd9da --- /dev/null +++ b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2023 Marian Postevca + */ + +#ifndef __ACP3X_ES83XX_H +#define __ACP3X_ES83XX_H + +void acp3x_es83xx_init_ops(struct acp_mach_ops *ops); + +#endif + From patchwork Mon Mar 20 20:35:19 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marian Postevca X-Patchwork-Id: 666184 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id E53C3C6FD1C for ; Mon, 20 Mar 2023 20:38:25 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 94B981F0; Mon, 20 Mar 2023 21:37:33 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 94B981F0 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1679344703; bh=WSkN0ZZK6/27eS+cWv7kIvWxbi85TDSr0hkiRngacrI=; h=From:To:Subject:Date:In-Reply-To:References:CC:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=ke+X/SVHGcjzggVcKAMR1ErgEMj7toA+TtjXbxQ5xqfyYQEkOTt2v9Jv/FWxnUEfl S4D1f/GphRnmLrR5T+qpymvqwVm1LBkISFfZxjbJmz6+eWU4RgVuWgTnPP7evot/sM dv/ZZ44obfnSFo/Ef2WYkzeBDbYP0hABSYYVFYMA= Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id 5B4B6F8055A; Mon, 20 Mar 2023 21:36:28 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id D69D6F80482; Mon, 20 Mar 2023 21:36:21 +0100 (CET) Received: from mail.mutex.one (mail.mutex.one [62.77.152.124]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 246FEF80482 for ; Mon, 20 Mar 2023 21:35:31 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 246FEF80482 Authentication-Results: alsa1.perex.cz; dkim=pass (1024-bit key, unprotected) header.d=mutex.one header.i=@mutex.one header.a=rsa-sha256 header.s=default header.b=i4J1qBnH Received: from localhost (localhost.localdomain [127.0.0.1]) by mail.mutex.one (Postfix) with ESMTP id B9F6516C0057; Mon, 20 Mar 2023 22:35:30 +0200 (EET) X-Virus-Scanned: Debian amavisd-new at mail.mutex.one Received: from mail.mutex.one ([127.0.0.1]) by localhost (mail.mutex.one [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id tLxfaqPRw7I4; Mon, 20 Mar 2023 22:35:28 +0200 (EET) From: Marian Postevca DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=mutex.one; s=default; t=1679344528; bh=WSkN0ZZK6/27eS+cWv7kIvWxbi85TDSr0hkiRngacrI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=i4J1qBnHV/8QMhVVNv+tnfYDl4MISC2dq6Jk1H6YB38pe/Ub5qsvS5QyK12mG7Qt6 eoGRRObhCCkyee/8nsXkutuOQtUkTi1YPcEWZOzm2V07LNERW4iTDUSdZFXZ42q4Kc NiIbGkVnbKHTNsUUegv38VN1fK1BvE7JKIwMwo6o= To: Takashi Iwai , Mark Brown , Liam Girdwood , Jaroslav Kysela Subject: [PATCH 4/4] ASoC: amd: acp: Improve support for speaker power events Date: Mon, 20 Mar 2023 22:35:19 +0200 Message-Id: <20230320203519.20137-5-posteuca@mutex.one> In-Reply-To: <20230320203519.20137-1-posteuca@mutex.one> References: <20230320203519.20137-1-posteuca@mutex.one> MIME-Version: 1.0 Message-ID-Hash: UAMYEESWG3DDOLDO6INSTIE4CXX53VAS X-Message-ID-Hash: UAMYEESWG3DDOLDO6INSTIE4CXX53VAS X-MailFrom: posteuca@mutex.one X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: linux-kernel@vger.kernel.org, alsa-devel@alsa-project.org, Marian Postevca X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: In order to reduce the audible pops when speaker or headphones are activated or disabled we need to delay the switching of the GPIOs. We need to also disable/enable the speaker/headphones GPIOs when the audio stream is stopped/started. To avoid race conditions between the speaker power event callback and the trigger callback we use a ring buffer to save the events that we need to process in the delayed work callback. Signed-off-by: Marian Postevca --- sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c | 154 +++++++++++++++++- 1 file changed, 149 insertions(+), 5 deletions(-) diff --git a/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c index 138a7c12669b..033c94e91928 100644 --- a/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c +++ b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c @@ -20,8 +20,18 @@ #include #include #include +#include + #include "../acp-mach.h" +#define RB_SIZE 32 + +#define circ_count(circ) \ + (CIRC_CNT((circ)->head, (circ)->tail, RB_SIZE)) + +#define circ_space(circ) \ + (CIRC_SPACE((circ)->head, (circ)->tail, RB_SIZE)) + #define get_mach_priv(card) ((struct acp3x_es83xx_private *)((acp_get_drvdata(card))->mach_priv)) /* mclk-div-by-2 + terminating entry */ @@ -32,14 +42,35 @@ #define ES83XX_ENABLE_DMIC BIT(4) #define ES83XX_48_MHZ_MCLK BIT(5) +enum { + SPEAKER_ON = 0, + SPEAKER_OFF, + SPEAKER_SUSPEND, + SPEAKER_RESUME, + SPEAKER_MAX +}; + +const char *msg[SPEAKER_MAX] = { + "SPEAKER_ON", + "SPEAKER_OFF", + "SPEAKER_SUSPEND", + "SPEAKER_RESUME" +}; + struct acp3x_es83xx_private { unsigned long quirk; + bool speaker_on; + bool stream_suspended; struct snd_soc_component *codec; struct device *codec_dev; struct gpio_desc *gpio_speakers, *gpio_headphone; struct acpi_gpio_params enable_spk_gpio, enable_hp_gpio; struct acpi_gpio_mapping gpio_mapping[3]; struct snd_soc_dapm_route mic_map[2]; + struct delayed_work jack_work; + struct mutex rb_lock; + struct circ_buf gpio_rb; + u8 gpio_events_buf[RB_SIZE]; }; static const unsigned int rates[] = { @@ -86,6 +117,107 @@ static void acp3x_es83xx_set_gpios_values(struct acp3x_es83xx_private *priv, gpiod_set_value_cansleep(priv->gpio_headphone, headphone); } +static void acp3x_es83xx_rb_insert_evt(struct circ_buf *rb, u8 val) +{ + u8 *buf = rb->buf; + + if (circ_space(rb) == 0) { + /* make some space by dropping the oldest entry, we are more + * interested in the last event + */ + rb->tail = (rb->tail + 1) & (RB_SIZE - 1); + } + buf[rb->head] = val; + rb->head = (rb->head + 1) & (RB_SIZE - 1); +} + +static int acp3x_es83xx_rb_remove_evt(struct circ_buf *rb) +{ + u8 *buf = rb->buf; + int rc = -1; + + if (circ_count(rb)) { + rc = buf[rb->tail]; + rb->tail = (rb->tail + 1) & (RB_SIZE - 1); + } + return rc; +} + +static void acp3x_es83xx_jack_events(struct work_struct *work) +{ + struct acp3x_es83xx_private *priv = + container_of(work, struct acp3x_es83xx_private, jack_work.work); + int evt; + + mutex_lock(&priv->rb_lock); + do { + evt = acp3x_es83xx_rb_remove_evt(&priv->gpio_rb); + dev_dbg(priv->codec_dev, "jack event, %s\n", msg[evt]); + switch (evt) { + case SPEAKER_SUSPEND: + acp3x_es83xx_set_gpios_values(priv, 0, 0); + break; + case SPEAKER_RESUME: + acp3x_es83xx_set_gpios_values(priv, priv->speaker_on, !priv->speaker_on); + break; + case SPEAKER_ON: + priv->speaker_on = true; + acp3x_es83xx_set_gpios_values(priv, 1, 0); + break; + case SPEAKER_OFF: + priv->speaker_on = false; + acp3x_es83xx_set_gpios_values(priv, 0, 1); + break; + } + } while (evt != -1); + mutex_unlock(&priv->rb_lock); +} + +static int acp3x_es83xx_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_card *card = rtd->card; + struct acp3x_es83xx_private *priv = get_mach_priv(card); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + if (substream->stream == 0) { + dev_dbg(priv->codec_dev, "trigger start/release/resume, activating GPIOs\n"); + mutex_lock(&priv->rb_lock); + if (priv->stream_suspended) { + priv->stream_suspended = false; + acp3x_es83xx_rb_insert_evt(&priv->gpio_rb, SPEAKER_RESUME); + mutex_unlock(&priv->rb_lock); + queue_delayed_work(system_wq, &priv->jack_work, + msecs_to_jiffies(1)); + } else { + mutex_unlock(&priv->rb_lock); + } + } + + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + if (substream->stream == 0) { + dev_dbg(priv->codec_dev, "trigger pause/suspend/stop deactivating GPIOs\n"); + mutex_lock(&priv->rb_lock); + priv->stream_suspended = true; + acp3x_es83xx_rb_insert_evt(&priv->gpio_rb, SPEAKER_SUSPEND); + mutex_unlock(&priv->rb_lock); + queue_delayed_work(system_wq, &priv->jack_work, msecs_to_jiffies(1)); + } + break; + default: + return -EINVAL; + } + + return 0; +} + static int acp3x_es83xx_create_swnode(struct device *codec_dev) { struct property_entry props[MAX_NO_PROPS] = {}; @@ -212,12 +344,17 @@ static int acp3x_es83xx_speaker_power_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct acp3x_es83xx_private *priv = get_mach_priv(w->dapm->card); + u8 val; + + val = SND_SOC_DAPM_EVENT_ON(event) ? SPEAKER_ON : SPEAKER_OFF; - dev_dbg(priv->codec_dev, "speaker power event: %d\n", event); - if (SND_SOC_DAPM_EVENT_ON(event)) - acp3x_es83xx_set_gpios_values(priv, 1, 0); - else - acp3x_es83xx_set_gpios_values(priv, 0, 1); + dev_dbg(priv->codec_dev, "speaker power event = %s\n", msg[val]); + + mutex_lock(&priv->rb_lock); + acp3x_es83xx_rb_insert_evt(&priv->gpio_rb, val); + mutex_unlock(&priv->rb_lock); + + queue_delayed_work(system_wq, &priv->jack_work, msecs_to_jiffies(70)); return 0; } @@ -353,6 +490,7 @@ static int acp3x_es83xx_init(struct snd_soc_pcm_runtime *runtime) static const struct snd_soc_ops acp3x_es83xx_ops = { .startup = acp3x_es83xx_codec_startup, + .trigger = acp3x_es83xx_trigger, }; @@ -452,6 +590,12 @@ static int acp3x_es83xx_probe(struct snd_soc_card *card) priv->codec_dev = codec_dev; priv->quirk = (unsigned long)dmi_id->driver_data; acp_drvdata->mach_priv = priv; + + priv->gpio_rb.buf = priv->gpio_events_buf; + priv->gpio_rb.head = priv->gpio_rb.tail = 0; + mutex_init(&priv->rb_lock); + + INIT_DELAYED_WORK(&priv->jack_work, acp3x_es83xx_jack_events); dev_info(dev, "successfully probed the sound card\n"); } else { ret = -ENODEV;