From patchwork Thu Mar 31 00:04:45 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Martin_Povi=C5=A1er?= X-Patchwork-Id: 555306 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 7AF84C433F5 for ; Thu, 31 Mar 2022 00:07:04 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 80B701948; Thu, 31 Mar 2022 02:06:12 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 80B701948 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1648685222; bh=+KmEFqmIdDsWpjx370TkKd/+8aZ8TQBHU8o77qI8xBU=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=PTejmae5vfapWCutUC/EHiHb61lbHWsQiRvBnqYB+hzliHpnKc03RD6eg1ncoTqPg sSz+xavQUfZ+uBKdarVOjwBQ9eB5MtvRaxJnZ2l2n2/g+7DI08DJ6y/0t6qMzOCMOj WB533KIPmF+bsspHXkKyLZTHbbeBDSTz0GIqB/7w= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id A08E2F8050F; Thu, 31 Mar 2022 02:05:39 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id DFA00F80516; Thu, 31 Mar 2022 02:05:37 +0200 (CEST) Received: from hutie.ust.cz (unknown [IPv6:2a03:3b40:fe:f0::1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id A4C38F80253 for ; Thu, 31 Mar 2022 02:05:31 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz A4C38F80253 Authentication-Results: alsa1.perex.cz; dkim=pass (1024-bit key) header.d=cutebit.org header.i=@cutebit.org header.b="QCK9u2U6" From: =?utf-8?q?Martin_Povi=C5=A1er?= DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cutebit.org; s=mail; t=1648685130; bh=vNAuOBEbxoca294MRvbJQo3FQJuHQNSkCGQ2YVXzgTs=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=QCK9u2U6MJT4PWy9Qe6btEbbolw5xt6EHdkL9DoSrAieQ0zCSYIXXtWbxiyAU78/n Mbkx0pcPFKwPWzSJPpP/C/dO4TjL3rdw72rss1RtR6LKROyfE+kNcTZn6zX0WxwSXo wSeWd9SXuI3BKym2mmg9ajUy4GWtEXxJ2HfBypN8= To: Liam Girdwood , Mark Brown , Rob Herring , Krzysztof Kozlowski , Jaroslav Kysela , Takashi Iwai Subject: [RFC PATCH 1/5] dt-bindings: sound: Add Apple Macs sound system Date: Thu, 31 Mar 2022 02:04:45 +0200 Message-Id: <20220331000449.41062-2-povik+lin@cutebit.org> In-Reply-To: <20220331000449.41062-1-povik+lin@cutebit.org> References: <20220331000449.41062-1-povik+lin@cutebit.org> MIME-Version: 1.0 Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org, Sven Peter , Hector Martin , linux-kernel@vger.kernel.org, Mark Kettenis , =?utf-8?q?Martin_Povi=C5=A1er?= X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" Add binding for Apple Silicon Macs' machine-level sound system. Signed-off-by: Martin Povišer --- .../bindings/sound/apple,macaudio.yaml | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/apple,macaudio.yaml diff --git a/Documentation/devicetree/bindings/sound/apple,macaudio.yaml b/Documentation/devicetree/bindings/sound/apple,macaudio.yaml new file mode 100644 index 000000000000..a6380e4bdd1a --- /dev/null +++ b/Documentation/devicetree/bindings/sound/apple,macaudio.yaml @@ -0,0 +1,103 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/apple,macaudio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Apple Silicon Macs integrated sound system + +maintainers: + - Martin Povišer + +definitions: + dai: + type: object + properties: + sound-dai: true + required: + - sound-dai + +properties: + compatible: + items: + - enum: + - apple,j274-macaudio + - apple,j293-macaudio + - apple,j314-macaudio + - const: apple,macaudio + "#address-cells": + const: 1 + "#size-cells": + const: 0 + model: + description: | + Model name to use when the sound system is presented to users as a sound card. + $ref: /schemas/types.yaml#/definitions/string + +patternProperties: + "^dai-link(@[0-9a-f]+)?$": + description: | + A DAI link comprising of CPU and CODEC DAI specifiers and supplemental properties. + type: object + properties: + reg: + maxItems: 1 + mclk-fs: + description: | + Forced MCLK/samplerate factor (optional). + $ref: /schemas/types.yaml#/definitions/uint32 + link-name: + description: Name for the DAI link to present to users. + $ref: /schemas/types.yaml#/definitions/string + cpu: + $ref: "#/definitions/dai" + codec: + $ref: "#/definitions/dai" + required: + - reg + - cpu + - codec + additionalProperties: false + +required: + - compatible + - model + +additionalProperties: false + +examples: + - | + sound { + compatible = "apple,j293-macaudio", "apple,macaudio"; + model = "MacBook Pro J293 integrated audio"; + + #address-cells = <1>; + #size-cells = <0>; + + dai-link@0 { + reg = <0>; + link-name = "Speakers"; + mclk-fs = <64>; + + cpu { + sound-dai = <&mca 0>, <&mca 1>; + }; + codec { + sound-dai = <&speaker_left_front>, <&speaker_right_front>, + <&speaker_left_rear>, <&speaker_right_rear>; + }; + }; + + dai-link@1 { + reg = <1>; + link-name = "Headphones Jack"; + mclk-fs = <64>; + + cpu { + sound-dai = <&mca 2>; + }; + codec { + sound-dai = <&jack_codec>; + }; + }; + }; From patchwork Thu Mar 31 00:04:46 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Martin_Povi=C5=A1er?= X-Patchwork-Id: 556257 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 9C926C433F5 for ; Thu, 31 Mar 2022 00:07:19 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id E68281A4C; Thu, 31 Mar 2022 02:06:27 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz E68281A4C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1648685238; bh=rBZ5kdZEm45+7toWL2Y9t32Mwdi3yHCkGCKxOzwcfqc=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=CrG58Sax412ILyfwl5ozjaV+9CLnnpunoPefXS91Jui5bv1vzVieoDSIEBRY36HqO D6NrWGmnqPL15GlMEQxzSuUwp724O5EqOqDQAHIZaLpgB80DogXSzlxsGJf76QhwyK 0QCXie9QnM44qHWawMqphouODiLaoclEQb+6tzQw= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 77AD6F80518; Thu, 31 Mar 2022 02:05:42 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 77331F80516; Thu, 31 Mar 2022 02:05:39 +0200 (CEST) Received: from hutie.ust.cz (unknown [IPv6:2a03:3b40:fe:f0::1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 3752FF80155 for ; Thu, 31 Mar 2022 02:05:31 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 3752FF80155 Authentication-Results: alsa1.perex.cz; dkim=pass (1024-bit key) header.d=cutebit.org header.i=@cutebit.org header.b="FZrLxYPz" From: =?utf-8?q?Martin_Povi=C5=A1er?= DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cutebit.org; s=mail; t=1648685131; bh=oMsX2lTEPQEK2x2Rm91zW3XEoYZk41C902ihhdHgcmY=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=FZrLxYPzeBxfH6RbWuq4gr3J4obaU7b9fmLpOLOA+Sa2UZy7WmQ9VcEvOAL0m65EB flON1zWDnWeDV6WRuy5zBRelHYv20CzbsWGEQT6jylhYjjkaR5+RQnVpANfFbmbR9d Yr6Hc1aFeAEakrYcVoDV0EXf4zNTewH/E0nMf6Iw= To: Liam Girdwood , Mark Brown , Rob Herring , Krzysztof Kozlowski , Jaroslav Kysela , Takashi Iwai Subject: [RFC PATCH 2/5] HACK: ASoC: Add card->filter_controls hook Date: Thu, 31 Mar 2022 02:04:46 +0200 Message-Id: <20220331000449.41062-3-povik+lin@cutebit.org> In-Reply-To: <20220331000449.41062-1-povik+lin@cutebit.org> References: <20220331000449.41062-1-povik+lin@cutebit.org> MIME-Version: 1.0 Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org, Sven Peter , Hector Martin , linux-kernel@vger.kernel.org, Mark Kettenis , =?utf-8?q?Martin_Povi=C5=A1er?= X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" Add a new ASoC card callback for filtering the kcontrols of the card's constituent components. This lets the card take over some of the controls, deciding their value instead of leaving it up to userspace. Also, and here's the HACK: part, move dapm_new_widgets call in front of the card's late_probe call. This way all kcontrols should have been created (and are safe to use) by the time late_probe is called. Signed-off-by: Martin Povišer --- include/sound/soc.h | 3 +++ sound/soc/soc-core.c | 45 +++++++++++++++++++++++++++----------------- sound/soc/soc-dapm.c | 34 ++++++++++++++++++++++++++++----- 3 files changed, 60 insertions(+), 22 deletions(-) diff --git a/include/sound/soc.h b/include/sound/soc.h index 7a1650b303f1..0ab664500b8f 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -897,6 +897,9 @@ struct snd_soc_card { int (*late_probe)(struct snd_soc_card *card); int (*remove)(struct snd_soc_card *card); + int (*filter_controls)(struct snd_soc_card *card, + struct snd_kcontrol *kcontrol); + /* the pre and post PM functions are used to do any PM work before and * after the codec and DAI's do any PM work. */ int (*suspend_pre)(struct snd_soc_card *card); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index a088bc9f7dd7..0bf05d98ec0d 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2069,12 +2069,12 @@ static int snd_soc_bind_card(struct snd_soc_card *card) } } + snd_soc_dapm_new_widgets(card); + ret = snd_soc_card_late_probe(card); if (ret < 0) goto probe_end; - snd_soc_dapm_new_widgets(card); - ret = snd_card_register(card->snd_card); if (ret < 0) { dev_err(card->dev, "ASoC: failed to register soundcard %d\n", @@ -2209,19 +2209,34 @@ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, } EXPORT_SYMBOL_GPL(snd_soc_cnew); -static int snd_soc_add_controls(struct snd_card *card, struct device *dev, +static int snd_soc_add_controls(struct snd_soc_card *card, struct device *dev, const struct snd_kcontrol_new *controls, int num_controls, const char *prefix, void *data) { - int i; + int i, err; for (i = 0; i < num_controls; i++) { - const struct snd_kcontrol_new *control = &controls[i]; - int err = snd_ctl_add(card, snd_soc_cnew(control, data, - control->name, prefix)); + const struct snd_kcontrol_new *control_new = &controls[i]; + struct snd_kcontrol *control; + + control = snd_soc_cnew(control_new, data, + control_new->name, prefix); + + if (card->filter_controls) { + err = card->filter_controls(card, control); + if (err < 0) { + snd_ctl_free_one(control); + return err; + } else if (err) { + continue; + } + } + + err = snd_ctl_add(card->snd_card, control); + if (err < 0) { dev_err(dev, "ASoC: Failed to add %s: %d\n", - control->name, err); + control_new->name, err); return err; } } @@ -2241,9 +2256,7 @@ static int snd_soc_add_controls(struct snd_card *card, struct device *dev, int snd_soc_add_component_controls(struct snd_soc_component *component, const struct snd_kcontrol_new *controls, unsigned int num_controls) { - struct snd_card *card = component->card->snd_card; - - return snd_soc_add_controls(card, component->dev, controls, + return snd_soc_add_controls(component->card, component->dev, controls, num_controls, component->name_prefix, component); } EXPORT_SYMBOL_GPL(snd_soc_add_component_controls); @@ -2258,13 +2271,11 @@ EXPORT_SYMBOL_GPL(snd_soc_add_component_controls); * * Return 0 for success, else error. */ -int snd_soc_add_card_controls(struct snd_soc_card *soc_card, +int snd_soc_add_card_controls(struct snd_soc_card *card, const struct snd_kcontrol_new *controls, int num_controls) { - struct snd_card *card = soc_card->snd_card; - - return snd_soc_add_controls(card, soc_card->dev, controls, num_controls, - NULL, soc_card); + return snd_soc_add_controls(card, card->dev, controls, num_controls, + NULL, card); } EXPORT_SYMBOL_GPL(snd_soc_add_card_controls); @@ -2281,7 +2292,7 @@ EXPORT_SYMBOL_GPL(snd_soc_add_card_controls); int snd_soc_add_dai_controls(struct snd_soc_dai *dai, const struct snd_kcontrol_new *controls, int num_controls) { - struct snd_card *card = dai->component->card->snd_card; + struct snd_soc_card *card = dai->component->card; return snd_soc_add_controls(card, dai->dev, controls, num_controls, NULL, dai); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index b06c5682445c..56ecbabe5453 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -873,7 +873,7 @@ static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w, int kci) { struct snd_soc_dapm_context *dapm = w->dapm; - struct snd_card *card = dapm->card->snd_card; + struct snd_soc_card *card = dapm->card; const char *prefix; size_t prefix_len; int shared; @@ -957,7 +957,19 @@ static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w, goto exit_free; } - ret = snd_ctl_add(card, kcontrol); + if (card->filter_controls) { + ret = card->filter_controls(card, kcontrol); + if (ret < 0) { + snd_ctl_free_one(kcontrol); + goto exit_free; + } + + if (!ret) + ret = snd_ctl_add(card->snd_card, kcontrol); + } else { + ret = snd_ctl_add(card->snd_card, kcontrol); + } + if (ret < 0) { dev_err(dapm->dev, "ASoC: failed to add widget %s dapm kcontrol %s: %d\n", @@ -1074,7 +1086,7 @@ static int dapm_new_pga(struct snd_soc_dapm_widget *w) /* create new dapm dai link control */ static int dapm_new_dai_link(struct snd_soc_dapm_widget *w) { - int i; + int i, ret; struct snd_soc_pcm_runtime *rtd = w->priv; /* create control for links with > 1 config */ @@ -1084,10 +1096,22 @@ static int dapm_new_dai_link(struct snd_soc_dapm_widget *w) /* add kcontrol */ for (i = 0; i < w->num_kcontrols; i++) { struct snd_soc_dapm_context *dapm = w->dapm; - struct snd_card *card = dapm->card->snd_card; + struct snd_soc_card *card = dapm->card; struct snd_kcontrol *kcontrol = snd_soc_cnew(&w->kcontrol_news[i], w, w->name, NULL); - int ret = snd_ctl_add(card, kcontrol); + + if (card->filter_controls) { + ret = card->filter_controls(card, kcontrol); + if (ret < 0) { + snd_ctl_free_one(kcontrol); + return ret; + } + + if (!ret) + ret = snd_ctl_add(card->snd_card, kcontrol); + } else { + ret = snd_ctl_add(card->snd_card, kcontrol); + } if (ret < 0) { dev_err(dapm->dev, From patchwork Thu Mar 31 00:04:47 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Martin_Povi=C5=A1er?= X-Patchwork-Id: 555305 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 957EFC433FE for ; Thu, 31 Mar 2022 00:07:22 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id BE6D41AB4; Thu, 31 Mar 2022 02:06:30 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz BE6D41AB4 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1648685240; bh=kBqmLJyK45UwHLatmSNkgmTzdLqU4v/Zl/8onBfplDk=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=Y6KYFkwGHuR7D5JInKAPu8w1NrPRQibl9G0iAMtFkUlOYXv7L0dPK7JakADszS7Qr 9/WSDp8e5cZlgfGGM2bTuSphWW447yVRcnBmNQEsPLRZMExVmB64t9lmDaKfOyRA2d PPvWp5tBLq6/axlydyY2o+FTCwGcVYRwIN9TiZDY= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 10344F80519; Thu, 31 Mar 2022 02:05:43 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id BE992F80518; Thu, 31 Mar 2022 02:05:39 +0200 (CEST) Received: from hutie.ust.cz (unknown [IPv6:2a03:3b40:fe:f0::1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 98901F80254 for ; Thu, 31 Mar 2022 02:05:32 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 98901F80254 Authentication-Results: alsa1.perex.cz; dkim=pass (1024-bit key) header.d=cutebit.org header.i=@cutebit.org header.b="aN/8a0qf" From: =?utf-8?q?Martin_Povi=C5=A1er?= DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cutebit.org; s=mail; t=1648685132; bh=SeiIUiZeJW/WU1eEvgOIqMuWCeT5CiLubwaCNwTIB44=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=aN/8a0qfTlacb8dkUsL9qNzNhslN5RAhSevBw6Ynn92QaDWLBR/+XW0cT6sDqo6Mk E2/xOWMdeUhPjHnUAIK+KYx93csU11L+jqfr5v7cswdYDU5sktP9sBxD8hG1hNDJkl I7PW6+g6HIA4W7v9ejkAvnTSXoyvnLB22q6pz2To= To: Liam Girdwood , Mark Brown , Rob Herring , Krzysztof Kozlowski , Jaroslav Kysela , Takashi Iwai Subject: [RFC PATCH 3/5] HACK: ASoC: Tolerate N-cpus-to-M-codecs links Date: Thu, 31 Mar 2022 02:04:47 +0200 Message-Id: <20220331000449.41062-4-povik+lin@cutebit.org> In-Reply-To: <20220331000449.41062-1-povik+lin@cutebit.org> References: <20220331000449.41062-1-povik+lin@cutebit.org> MIME-Version: 1.0 Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org, Sven Peter , Hector Martin , linux-kernel@vger.kernel.org, Mark Kettenis , =?utf-8?q?Martin_Povi=C5=A1er?= X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" Tolerate N-to-M DAI links while using the first CPU DAI to decide playback/capture abilities. Signed-off-by: Martin Povišer --- sound/soc/soc-pcm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 9a954680d492..770cf367a147 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2781,9 +2781,12 @@ static int soc_get_playback_capture(struct snd_soc_pcm_runtime *rtd, } else if (rtd->num_cpus == rtd->num_codecs) { cpu_dai = asoc_rtd_to_cpu(rtd, i); } else { +#if 0 dev_err(rtd->card->dev, "N cpus to M codecs link is not supported yet\n"); return -EINVAL; +#endif + cpu_dai = asoc_rtd_to_cpu(rtd, 0); } if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) && From patchwork Thu Mar 31 00:04:48 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Martin_Povi=C5=A1er?= X-Patchwork-Id: 555304 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 58DFCC433EF for ; Thu, 31 Mar 2022 00:08:12 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 90F411AD3; Thu, 31 Mar 2022 02:07:20 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 90F411AD3 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1648685290; bh=+fNKPocUjxQXDuz3GDZUxg0u1ctHRF6erM6q0fnGmzI=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=jr0hAJbPwK7AzbXP3GyX79oVFG7kTczac2LQ0awAdPDsrQjseOORL1U/Jts58oBKH mvskzqpIVEZtHO2kyFOPn2rgkOgLUksOr9G2jsqbOgv3Jyj7bpfdewEqyFvOJlUVAw kvZ5GeMYGjQggQm8Jq90W7RhfbiO2b6XdNz1NWsQ= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id D2473F80534; Thu, 31 Mar 2022 02:05:48 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 9F5B5F8051D; Thu, 31 Mar 2022 02:05:45 +0200 (CEST) Received: from hutie.ust.cz (hutie.ust.cz [185.8.165.127]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 45339F80253 for ; Thu, 31 Mar 2022 02:05:33 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 45339F80253 Authentication-Results: alsa1.perex.cz; dkim=pass (1024-bit key) header.d=cutebit.org header.i=@cutebit.org header.b="VpQis266" From: =?utf-8?q?Martin_Povi=C5=A1er?= DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cutebit.org; s=mail; t=1648685133; bh=Ea0QBaJP2Xhb2qk22MNKaoz+blbZuooVi0kGOsoChyQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=VpQis266M9BalLOWg7P4c+68owWtkGGUriBUeUvRyIyl43yyS9AeR6+npb/uhLoFt cMdw2Woc8wg62T4OF3zn6GU3pC59+r9ZlLV67mAu/hz1u2cugNWW+fk+SqwpnZqlhG tWrI+oOjiZ4p8wuTk6iLkVWaYDX6HAojQwwwLI0o= To: Liam Girdwood , Mark Brown , Rob Herring , Krzysztof Kozlowski , Jaroslav Kysela , Takashi Iwai Subject: [RFC PATCH 4/5] ASoC: Introduce snd_soc_of_get_dai_link_cpus Date: Thu, 31 Mar 2022 02:04:48 +0200 Message-Id: <20220331000449.41062-5-povik+lin@cutebit.org> In-Reply-To: <20220331000449.41062-1-povik+lin@cutebit.org> References: <20220331000449.41062-1-povik+lin@cutebit.org> MIME-Version: 1.0 Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org, Sven Peter , Hector Martin , linux-kernel@vger.kernel.org, Mark Kettenis , =?utf-8?q?Martin_Povi=C5=A1er?= X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" This function is an analogue of snd_soc_of_get_dai_link_codecs to help machine drivers read CPU DAI lists from devicetrees. Signed-off-by: Martin Povišer --- include/sound/soc.h | 4 +++ sound/soc/soc-core.c | 80 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/include/sound/soc.h b/include/sound/soc.h index 0ab664500b8f..0bf9d1d0768c 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1266,6 +1266,10 @@ int snd_soc_of_get_dai_link_codecs(struct device *dev, struct device_node *of_node, struct snd_soc_dai_link *dai_link); void snd_soc_of_put_dai_link_codecs(struct snd_soc_dai_link *dai_link); +int snd_soc_of_get_dai_link_cpus(struct device *dev, + struct device_node *of_node, + struct snd_soc_dai_link *dai_link); +void snd_soc_of_put_dai_link_cpus(struct snd_soc_dai_link *dai_link); int snd_soc_add_pcm_runtime(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 0bf05d98ec0d..a97476ec01ab 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3400,6 +3400,86 @@ int snd_soc_of_get_dai_link_codecs(struct device *dev, } EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_link_codecs); +/* + * snd_soc_of_put_dai_link_cpus - Dereference device nodes in the codecs array + * @dai_link: DAI link + * + * Dereference device nodes acquired by snd_soc_of_get_dai_link_cpus(). + */ +void snd_soc_of_put_dai_link_cpus(struct snd_soc_dai_link *dai_link) +{ + struct snd_soc_dai_link_component *component; + int index; + + for_each_link_cpus(dai_link, index, component) { + if (!component->of_node) + break; + of_node_put(component->of_node); + component->of_node = NULL; + } +} +EXPORT_SYMBOL_GPL(snd_soc_of_put_dai_link_cpus); + +/* + * snd_soc_of_get_dai_link_cpus - Parse a list of CPU DAIs in the devicetree + * @dev: Card device + * @of_node: Device node + * @dai_link: DAI link + * + * Is analogous to snd_soc_of_get_dai_link_codecs but parses a list of CPU DAIs + * instead. + * + * Returns 0 for success + */ +int snd_soc_of_get_dai_link_cpus(struct device *dev, + struct device_node *of_node, + struct snd_soc_dai_link *dai_link) +{ + struct of_phandle_args args; + struct snd_soc_dai_link_component *component; + char *name; + int index, num_codecs, ret; + + /* Count the number of CODECs */ + name = "sound-dai"; + num_codecs = of_count_phandle_with_args(of_node, name, + "#sound-dai-cells"); + if (num_codecs <= 0) { + if (num_codecs == -ENOENT) + dev_err(dev, "No 'sound-dai' property\n"); + else + dev_err(dev, "Bad phandle in 'sound-dai'\n"); + return num_codecs; + } + component = devm_kcalloc(dev, + num_codecs, sizeof(*component), + GFP_KERNEL); + if (!component) + return -ENOMEM; + dai_link->cpus = component; + dai_link->num_cpus = num_codecs; + + /* Parse the list */ + for_each_link_cpus(dai_link, index, component) { + ret = of_parse_phandle_with_args(of_node, name, + "#sound-dai-cells", + index, &args); + if (ret) + goto err; + component->of_node = args.np; + ret = snd_soc_get_dai_name(&args, &component->dai_name); + if (ret < 0) + goto err; + } + return 0; +err: + snd_soc_of_put_dai_link_codecs(dai_link); + dai_link->cpus = NULL; + dai_link->num_cpus = 0; + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_link_cpus); + static int __init snd_soc_init(void) { snd_soc_debugfs_init(); From patchwork Thu Mar 31 00:04:49 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Martin_Povi=C5=A1er?= X-Patchwork-Id: 556256 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 D0BE3C433F5 for ; Thu, 31 Mar 2022 00:07:49 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 130821AC4; Thu, 31 Mar 2022 02:06:58 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 130821AC4 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1648685268; bh=QACVLgk/k+ZBYUsGzYvAgMRgdqSPU5QGny9lejfo+ms=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=L8ebfnAZbIskLQnAw83SIhN5HW0TVG+MWfinSbznKu5UPU31P6YDgl6xYRZ5kNAwP HAIxmwwR1rRoToSdKx9uEHocD1QL45CxOXbrkl60vqjBUZmeQAGpt0Mv9x60oeEpBG cdxI8NdTkayo3Cttjd6pDWxPIpTgEVsZtwHYR59w= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id B154AF80524; Thu, 31 Mar 2022 02:05:47 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 2D71AF8051E; Thu, 31 Mar 2022 02:05:43 +0200 (CEST) Received: from hutie.ust.cz (unknown [IPv6:2a03:3b40:fe:f0::1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id C150DF804B4 for ; Thu, 31 Mar 2022 02:05:36 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz C150DF804B4 Authentication-Results: alsa1.perex.cz; dkim=pass (1024-bit key) header.d=cutebit.org header.i=@cutebit.org header.b="d5r9jICi" From: =?utf-8?q?Martin_Povi=C5=A1er?= DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cutebit.org; s=mail; t=1648685135; bh=5DCUaIsE4Uppw5JzTbkl1FjLIhH+WKKv/Ni/67xWdW0=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=d5r9jICimWOs8p7Oy+m+XL72g7YZXttPeUcxnRr1XBuSHJ4haxfbForiglmmAQ+t6 cPx/1kMxQHZ8jHB1ZOF+BKXGZwkYe2PMsDs28telVEtkKs8iKVMxHWIZsF0K8Nc20s 4hcRrP0376RGe/3Wqr9b0vyq6/qrPP4YijTdVQsQ= To: Liam Girdwood , Mark Brown , Rob Herring , Krzysztof Kozlowski , Jaroslav Kysela , Takashi Iwai Subject: [RFC PATCH 5/5] ASoC: Add macaudio machine driver Date: Thu, 31 Mar 2022 02:04:49 +0200 Message-Id: <20220331000449.41062-6-povik+lin@cutebit.org> In-Reply-To: <20220331000449.41062-1-povik+lin@cutebit.org> References: <20220331000449.41062-1-povik+lin@cutebit.org> MIME-Version: 1.0 Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org, Sven Peter , Hector Martin , linux-kernel@vger.kernel.org, Mark Kettenis , =?utf-8?q?Martin_Povi=C5=A1er?= X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" Add ASoC machine driver for Apple Silicon Macs. Signed-off-by: Martin Povišer --- sound/soc/apple/Kconfig | 10 + sound/soc/apple/Makefile | 3 + sound/soc/apple/macaudio.c | 597 +++++++++++++++++++++++++++++++++++++ 3 files changed, 610 insertions(+) create mode 100644 sound/soc/apple/Kconfig create mode 100644 sound/soc/apple/Makefile create mode 100644 sound/soc/apple/macaudio.c diff --git a/sound/soc/apple/Kconfig b/sound/soc/apple/Kconfig new file mode 100644 index 000000000000..afc0243b9309 --- /dev/null +++ b/sound/soc/apple/Kconfig @@ -0,0 +1,10 @@ +config SND_SOC_APPLE_MACAUDIO + tristate "ASoC machine driver for Apple Silicon Macs" + depends on ARCH_APPLE || COMPILE_TEST + select SND_SOC_APPLE_MCA + select SND_SIMPLE_CARD_UTILS + select APPLE_ADMAC + select COMMON_CLK_APPLE_NCO + default ARCH_APPLE + help + This option enables an ASoC machine driver for Apple Silicon Macs. diff --git a/sound/soc/apple/Makefile b/sound/soc/apple/Makefile new file mode 100644 index 000000000000..d7a2df6311b5 --- /dev/null +++ b/sound/soc/apple/Makefile @@ -0,0 +1,3 @@ +snd-soc-macaudio-objs := macaudio.o + +obj-$(CONFIG_SND_SOC_APPLE_MACAUDIO) += snd-soc-macaudio.o diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c new file mode 100644 index 000000000000..3e80f97a9b75 --- /dev/null +++ b/sound/soc/apple/macaudio.c @@ -0,0 +1,597 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ASoC machine driver for Apple Silicon Macs + * + * Copyright (C) The Asahi Linux Contributors + * + * Based on sound/soc/qcom/{sc7180.c|common.c} + * + * Copyright (c) 2018, Linaro Limited. + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "snd-soc-macaudio" + +struct macaudio_snd_data { + struct snd_soc_card card; + struct snd_soc_jack_pin pin; + struct snd_soc_jack jack; + + struct macaudio_link_props { + unsigned int mclk_fs; + } *link_props; + + const struct snd_pcm_chmap_elem *speaker_chmap; + + unsigned int speaker_nchans_array[2]; + struct snd_pcm_hw_constraint_list speaker_nchans_list; + + struct list_head hidden_kcontrols; +}; + +static int macaudio_parse_of(struct macaudio_snd_data *ma, struct snd_soc_card *card) +{ + struct device_node *np; + struct device_node *codec = NULL; + struct device_node *cpu = NULL; + struct device *dev = card->dev; + struct snd_soc_dai_link *link; + struct macaudio_link_props *link_props; + int ret, num_links; + int i = 0; + + ret = snd_soc_of_parse_card_name(card, "model"); + if (ret) { + dev_err(dev, "Error parsing card name: %d\n", ret); + return ret; + } + + ret = asoc_simple_parse_routing(card, NULL); + if (ret) + return ret; + + /* Populate links */ + num_links = of_get_available_child_count(dev->of_node); + + /* Allocate the DAI link array */ + card->dai_link = devm_kcalloc(dev, num_links, sizeof(*link), GFP_KERNEL); + ma->link_props = devm_kcalloc(dev, num_links, sizeof(*ma->link_props), GFP_KERNEL); + if (!card->dai_link || !ma->link_props) + return -ENOMEM; + + card->num_links = num_links; + link = card->dai_link; + link_props = ma->link_props; + + for_each_available_child_of_node(dev->of_node, np) { + link->id = i++; + + /* CPU side is bit and frame clock master, I2S with both clocks inverted */ + link->dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_CBC_CFC | + SND_SOC_DAIFMT_GATED | + SND_SOC_DAIFMT_IB_IF; + + ret = of_property_read_string(np, "link-name", &link->name); + if (ret) { + dev_err(card->dev, "Missing link name\n"); + goto err_put_np; + } + + cpu = of_get_child_by_name(np, "cpu"); + codec = of_get_child_by_name(np, "codec"); + + if (!codec || !cpu) { + dev_err(dev, "Missing DAI specifications for '%s'\n", link->name); + ret = -EINVAL; + goto err; + } + + ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(card->dev, "%s: codec dai not found: %d\n", + link->name, ret); + goto err; + } + + ret = snd_soc_of_get_dai_link_cpus(dev, cpu, link); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(card->dev, "%s: cpu dai not found: %d\n", + link->name, ret); + goto err; + } + + link->num_platforms = 1; + link->platforms = devm_kzalloc(dev, sizeof(*link->platforms), + GFP_KERNEL); + if (!link->platforms) { + ret = -ENOMEM; + goto err; + } + link->platforms->of_node = link->cpus->of_node; + + of_property_read_u32(np, "mclk-fs", &link_props->mclk_fs); + + link->stream_name = link->name; + link++; + link_props++; + + of_node_put(cpu); + of_node_put(codec); + } + + /* + * TODO: Not sure I shouldn't do something about the ->of_node component + * references I leave in dai_link (if successful here). + */ + + return 0; +err: + of_node_put(cpu); + of_node_put(codec); +err_put_np: + for (i = 0; i < num_links; i++) { + snd_soc_of_put_dai_link_codecs(&card->dai_link[i]); + snd_soc_of_put_dai_link_cpus(&card->dai_link[i]); + } + of_node_put(np); + return ret; +} + +static int macaudio_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(rtd->card); + struct macaudio_link_props *props = &ma->link_props[rtd->num]; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *dai; + int i, mclk; + + if (props->mclk_fs) { + mclk = params_rate(params) * props->mclk_fs; + + for_each_rtd_codec_dais(rtd, i, dai) + snd_soc_dai_set_sysclk(dai, 0, mclk, SND_SOC_CLOCK_IN); + + snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, SND_SOC_CLOCK_OUT); + } + + return 0; +} + +static void macaudio_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(rtd->card); + struct macaudio_link_props *props = &ma->link_props[rtd->num]; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *dai; + int i; + + if (props->mclk_fs) { + for_each_rtd_codec_dais(rtd, i, dai) + snd_soc_dai_set_sysclk(dai, 0, 0, SND_SOC_CLOCK_IN); + + snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_OUT); + } +} + +static bool macaudio_is_speakers(struct snd_soc_dai_link *dai_link) +{ + return !strcmp(rtd->dai_link->name, "Speaker") + || !strcmp(rtd->dai_link->name, "Speakers"); +} + +static int macaudio_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_card *card = rtd->card; + struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); + struct snd_pcm_hw_constraint_list *nchans_list = &ma->speaker_nchans_list; + unsigned int *nchans_array = ma->speaker_nchans_array; + int ret; + + if (macaudio_is_speakers(rtd->dai_link)) { + if (rtd->num_codecs > 2) { + nchans_list->count = 2; + nchans_list->list = nchans_array; + nchans_array[0] = 2; + nchans_array[1] = rtd->num_codecs; + + ret = snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, nchans_list); + if (ret < 0) + return ret; + } else if (rtd->num_codecs == 2) { + ret = snd_pcm_hw_constraint_single(substream->runtime, + SNDRV_PCM_HW_PARAM_CHANNELS, 2); + if (ret < 0) + return ret; + } + } + + return 0; +} + +static int macaudio_assign_tdm(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct snd_soc_dai *dai, *cpu_dai; + int ret, i; + int nchans = 0, nslots = 0, slot_width = 32; + + nslots = rtd->num_codecs; + + for_each_rtd_codec_dais(rtd, i, dai) { + int codec_nchans = 1; + int mask = ((1 << codec_nchans) - 1) << nchans; + + ret = snd_soc_dai_set_tdm_slot(dai, mask, + mask, nslots, slot_width); + if (ret == -EINVAL) + /* Try without the RX mask */ + ret = snd_soc_dai_set_tdm_slot(dai, mask, + 0, nslots, slot_width); + + if (ret < 0) { + dev_err(card->dev, "DAI %s refuses TDM settings: %d", + dai->name, ret); + return ret; + } + + nchans += codec_nchans; + } + + cpu_dai = asoc_rtd_to_cpu(rtd, 0); + ret = snd_soc_dai_set_tdm_slot(cpu_dai, (1 << nslots) - 1, + (1 << nslots) - 1, nslots, slot_width); + if (ret < 0) { + dev_err(card->dev, "CPU DAI %s refuses TDM settings: %d", + cpu_dai->name, ret); + return ret; + } + + return 0; +} + +static int macaudio_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component; + int ret, i; + + if (rtd->num_codecs > 1) { + ret = macaudio_assign_tdm(rtd); + if (ret < 0) + return ret; + } + + for_each_rtd_components(rtd, i, component) + snd_soc_component_set_jack(component, &ma->jack, NULL); + + return 0; +} + +static void macaudio_exit(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *component; + int i; + + for_each_rtd_components(rtd, i, component) + snd_soc_component_set_jack(component, NULL, NULL); +} + +struct macaudio_kctlfix { + char *name; + char *value; +} macaudio_kctlfixes[] = { + {"* ASI1 Sel", "Left"}, + {"* ISENSE Switch", "Off"}, + {"* VSENSE Switch", "Off"}, + { } +}; + +static bool macaudio_kctlfix_matches(const char *pattern, const char *name) +{ + if (pattern[0] == '*') { + int namelen, patternlen; + + pattern++; + if (pattern[0] == ' ') + pattern++; + + namelen = strlen(name); + patternlen = strlen(pattern); + + if (namelen > patternlen) + name += (namelen - patternlen); + } + + return !strcmp(name, pattern); +} + +static struct macaudio_kctlfix *macaudio_find_kctlfix(const char *name) +{ + struct macaudio_kctlfix *fctl; + + for (fctl = macaudio_kctlfixes; fctl->name != NULL; fctl++) + if (macaudio_kctlfix_matches(fctl->name, name)) + return fctl; + + return NULL; +} + +static int macaudio_probe(struct snd_soc_card *card) +{ + struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); + int ret; + + INIT_LIST_HEAD(&ma->hidden_kcontrols); + + ma->pin.pin = "Headphones"; + ma->pin.mask = SND_JACK_HEADSET | SND_JACK_HEADPHONE; + ret = snd_soc_card_jack_new(card, ma->pin.pin, + SND_JACK_HEADSET | + SND_JACK_HEADPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &ma->jack, &ma->pin, 1); + + if (ret < 0) + dev_err(card->dev, "jack creation failed: %d\n", ret); + + return ret; +} + +/* + * Maybe this could be a general ASoC function? + */ +static void snd_soc_kcontrol_set_strval(struct snd_soc_card *card, + struct snd_kcontrol *kcontrol, const char *strvalue) +{ + struct snd_ctl_elem_value value; + struct snd_ctl_elem_info info; + int sel, i, ret; + + ret = kcontrol->info(kcontrol, &info); + if (ret < 0) { + dev_err(card->dev, "can't obtain info on control '%s': %d", + kcontrol->id.name, ret); + return; + } + + switch (info.type) { + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: + for (sel = 0; sel < info.value.enumerated.items; sel++) { + info.value.enumerated.item = sel; + kcontrol->info(kcontrol, &info); + + if (!strcmp(strvalue, info.value.enumerated.name)) + break; + } + + if (sel == info.value.enumerated.items) + goto not_avail; + + for (i = 0; i < info.count; i++) + value.value.enumerated.item[i] = sel; + break; + + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: + sel = !strcmp(strvalue, "On"); + + if (!sel && strcmp(strvalue, "Off")) + goto not_avail; + + for (i = 0; i < info.count; i++) + value.value.integer.value[i] = sel; + break; + + case SNDRV_CTL_ELEM_TYPE_INTEGER: + if (kstrtoint(strvalue, 10, &sel)) + goto not_avail; + + for (i = 0; i < info.count; i++) + value.value.integer.value[i] = sel; + break; + + default: + dev_err(card->dev, "%s: control '%s' has unsupported type %d", + __func__, kcontrol->id.name, info.type); + return; + } + + ret = kcontrol->put(kcontrol, &value); + if (ret < 0) { + dev_err(card->dev, "can't set control '%s' to '%s': %d", + kcontrol->id.name, strvalue, ret); + return; + } + + dev_dbg(card->dev, "set '%s' to '%s'", + kcontrol->id.name, strvalue); + return; + +not_avail: + dev_err(card->dev, "option '%s' on control '%s' not available", + strvalue, kcontrol->id.name); + return; + +} + +static int macaudio_filter_controls(struct snd_soc_card *card, + struct snd_kcontrol *kcontrol) +{ + struct macaudio_kctlfix *fctl = macaudio_find_kctlfix(kcontrol->id.name); + struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); + + dev_dbg(card->dev, "visiting control %s, have match %d\n", + kcontrol->id.name, !!fctl); + + if (!fctl) + return 0; + + list_add_tail(&kcontrol->list, &ma->hidden_kcontrols); + return 1; +} + +static int macaudio_late_probe(struct snd_soc_card *card) +{ + struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); + struct snd_kcontrol *kcontrol; + struct snd_soc_pcm_runtime *rtd; + int ret; + + /* + * Here we take it to be okay to fiddle with the kcontrols + * we caught for ourselves. + */ + list_for_each_entry(kcontrol, &ma->hidden_kcontrols, list) { + struct macaudio_kctlfix *fctl = macaudio_find_kctlfix(kcontrol->id.name); + + if (fctl) + snd_soc_kcontrol_set_strval(card, kcontrol, fctl->value); + } + + for_each_card_rtds(card, rtd) { + if (macaudio_is_speakers(rtd->dai_link) && ma->speaker_chmap) { + ret = snd_pcm_add_chmap_ctls(rtd->pcm, + SNDRV_PCM_STREAM_PLAYBACK, ma->speaker_chmap, + rtd->num_codecs, 0, NULL); + if (ret < 0) + dev_err(card->dev, "failed to add channel map on '%s': %d\n", + rtd->dai_link->name, ret); + } + } + + return 0; +} + +static int macaudio_remove(struct snd_soc_card *card) +{ + struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); + struct snd_kcontrol *kcontrol; + + list_for_each_entry(kcontrol, &ma->hidden_kcontrols, list) + snd_ctl_free_one(kcontrol); + + return 0; +} + +static const struct snd_soc_ops macaudio_ops = { + .startup = macaudio_startup, + .shutdown = macaudio_shutdown, + .hw_params = macaudio_hw_params, +}; + +static const struct snd_soc_dapm_widget macaudio_snd_widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), +}; + +static const struct snd_pcm_chmap_elem macaudio_j274_chmaps[] = { + { .channels = 1, + .map = { SNDRV_CHMAP_MONO } }, + { } +}; + +static const struct snd_pcm_chmap_elem macaudio_j293_chmaps[] = { + { .channels = 2, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, + { .channels = 4, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, + SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, + { } +}; + +static const struct snd_pcm_chmap_elem macaudio_j314_chmaps[] = { + { .channels = 2, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, + { .channels = 6, + .map = { SNDRV_CHMAP_SL, SNDRV_CHMAP_SR, + SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, + SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, + { } +}; + +static const struct of_device_id macaudio_snd_device_id[] = { + { .compatible = "apple,j274-macaudio", .data = macaudio_j274_chmaps }, + { .compatible = "apple,j293-macaudio", .data = macaudio_j293_chmaps }, + { .compatible = "apple,j314-macaudio", .data = macaudio_j314_chmaps }, + { .compatible = "apple,macaudio", }, + { } +}; +MODULE_DEVICE_TABLE(of, macaudio_snd_device_id); + +static int macaudio_snd_platform_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + struct macaudio_snd_data *data; + struct device *dev = &pdev->dev; + struct snd_soc_dai_link *link; + const struct of_device_id *of_id; + int ret; + int i; + + of_id = of_match_device(macaudio_snd_device_id, dev); + if (!of_id) + return -EINVAL; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->speaker_chmap = of_id->data; + card = &data->card; + snd_soc_card_set_drvdata(card, data); + + card->owner = THIS_MODULE; + card->driver_name = DRIVER_NAME; + card->dev = dev; + card->dapm_widgets = macaudio_snd_widgets; + card->num_dapm_widgets = ARRAY_SIZE(macaudio_snd_widgets); + card->probe = macaudio_probe; + card->late_probe = macaudio_late_probe; + card->remove = macaudio_remove; + card->filter_controls = macaudio_filter_controls; + card->remove = macaudio_remove; + + ret = macaudio_parse_of(data, card); + if (ret) + return ret; + + for_each_card_prelinks(card, i, link) { + link->ops = &macaudio_ops; + link->init = macaudio_init; + link->exit = macaudio_exit; + } + + return devm_snd_soc_register_card(dev, card); +} + +static struct platform_driver macaudio_snd_driver = { + .probe = macaudio_snd_platform_probe, + .driver = { + .name = DRIVER_NAME, + .of_match_table = macaudio_snd_device_id, + .pm = &snd_soc_pm_ops, + }, +}; +module_platform_driver(macaudio_snd_driver); + +MODULE_AUTHOR("Martin Povišer "); +MODULE_DESCRIPTION("Apple Silicon Macs machine-level sound driver"); +MODULE_LICENSE("GPL");