Received: by 2002:a05:6a10:f3d0:0:0:0:0 with SMTP id a16csp3993555pxv; Mon, 28 Jun 2021 18:48:51 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxQgDwO6iFHZ48LMb8yYmsnThlLmXKxhUaNIGMKDiwKG5YC9PKKoCIMjKMcv3LSNx7w50Jv X-Received: by 2002:a02:8546:: with SMTP id g64mr2070842jai.35.1624931331193; Mon, 28 Jun 2021 18:48:51 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1624931331; cv=none; d=google.com; s=arc-20160816; b=oQ9aX9EqleeRK12Jgowr52Hfok6841O5AAbFIFQM2dVivnHz76TS+Pe8dFr2t/JvP5 wMvgMPKpIuZiRFNXImXNLiwWRVImpOKobo8ngxDjt7Q9TMGcAomI3TCJ2ErJtU6mkdUt fR4QZx5gZp76VhesDaDpcXn2H5PHwL5/Euawm+LuuMkhDpsOAG4dr3hXTK4oI0UhInX5 b/7o/SNKGG7zacqKOgT1S8o4aVMgLca418/OtEzTfJ0y2wAR6xs+tVivUhcLoQusDXuX sDfYim02DWl7j/qZl9vZUKim+vwWrKHifDcbdQgIwznhLjdScNA8c7w4g0+j82e40TWL E8CA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:mime-version:references:in-reply-to:message-id :date:subject:cc:to:from; bh=LTS3CEUS64qNFcL0wqeeRYuqbZtJTBTlLkk0TY8V8xE=; b=xoDglkDMAXAUO6kfQJvLtr5KCEwd4ET7/tZEq3s/6IIAIMfYalI/4NUTUQZe/T3nFR 5fwHU10r4f16NrT7agO6JG6ie2eU0EGdTWq9ay+yZXMY8XeKBsHdBlBOWOsZoTpGGyiI FccHdUsqGRMcYdq2hopn4/Sw4wozjQpyFhcvz7kYeq6NF6jqJSK1Fb4MjPiWjCeO9Fjg ++Wdph2lrtGOe3vs+/ZFRQ8IMpZQnTqg5o1g84PFpniqF5KSZ3yKRxlz7ZgPN+CyustP pPzPLcdBgR7WHugpsFnJIpVEGaolRmRZCiBJ6rgzEmE/RYWrM1bXbOpQNvc7VlQZ6/SH JnYg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=mediatek.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id l8si19150625ilk.17.2021.06.28.18.48.39; Mon, 28 Jun 2021 18:48:51 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=mediatek.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231332AbhF2Bu1 (ORCPT + 99 others); Mon, 28 Jun 2021 21:50:27 -0400 Received: from mailgw02.mediatek.com ([210.61.82.184]:43825 "EHLO mailgw02.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S230153AbhF2BuX (ORCPT ); Mon, 28 Jun 2021 21:50:23 -0400 X-UUID: db902d2dabce4739aac1f4d9e4b53a46-20210629 X-UUID: db902d2dabce4739aac1f4d9e4b53a46-20210629 Received: from mtkmbs10n1.mediatek.inc [(172.21.101.34)] by mailgw02.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256/256) with ESMTP id 519757739; Tue, 29 Jun 2021 09:47:53 +0800 Received: from mtkcas11.mediatek.inc (172.21.101.40) by mtkmbs06n1.mediatek.inc (172.21.101.129) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Tue, 29 Jun 2021 09:47:52 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas11.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1497.2 via Frontend Transport; Tue, 29 Jun 2021 09:47:51 +0800 From: Trevor Wu To: , , , CC: , , , , , , , , , Subject: [PATCH v2 4/8] ASoC: mediatek: mt8195: support pcm in platform driver Date: Tue, 29 Jun 2021 09:47:32 +0800 Message-ID: <20210629014736.31153-5-trevor.wu@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20210629014736.31153-1-trevor.wu@mediatek.com> References: <20210629014736.31153-1-trevor.wu@mediatek.com> MIME-Version: 1.0 Content-Type: text/plain X-MTK: N Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This patch adds mt8195 pcm dai driver. Signed-off-by: Trevor Wu --- sound/soc/mediatek/mt8195/mt8195-dai-pcm.c | 393 +++++++++++++++++++++ 1 file changed, 393 insertions(+) create mode 100644 sound/soc/mediatek/mt8195/mt8195-dai-pcm.c diff --git a/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c b/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c new file mode 100644 index 000000000000..bb0e2e4da14a --- /dev/null +++ b/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c @@ -0,0 +1,393 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek ALSA SoC Audio DAI PCM I/F Control + * + * Copyright (c) 2020 MediaTek Inc. + * Author: Bicycle Tsai + * Trevor Wu + */ + +#include +#include +#include "mt8195-afe-clk.h" +#include "mt8195-afe-common.h" +#include "mt8195-reg.h" + +enum { + MTK_DAI_PCM_FMT_I2S, + MTK_DAI_PCM_FMT_EIAJ, + MTK_DAI_PCM_FMT_MODEA, + MTK_DAI_PCM_FMT_MODEB, +}; + +enum { + MTK_DAI_PCM_CLK_A1SYS, + MTK_DAI_PCM_CLK_A2SYS, + MTK_DAI_PCM_CLK_26M_48K, + MTK_DAI_PCM_CLK_26M_441K, +}; + +struct mtk_dai_pcm_rate { + unsigned int rate; + unsigned int reg_value; +}; + +struct mtk_dai_pcmif_priv { + unsigned int slave_mode; + unsigned int lrck_inv; + unsigned int bck_inv; + unsigned int format; +}; + +static const struct mtk_dai_pcm_rate mtk_dai_pcm_rates[] = { + { .rate = 8000, .reg_value = 0, }, + { .rate = 16000, .reg_value = 1, }, + { .rate = 32000, .reg_value = 2, }, + { .rate = 48000, .reg_value = 3, }, + { .rate = 11025, .reg_value = 1, }, + { .rate = 22050, .reg_value = 2, }, + { .rate = 44100, .reg_value = 3, }, +}; + +static int mtk_dai_pcm_mode(unsigned int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mtk_dai_pcm_rates); i++) + if (mtk_dai_pcm_rates[i].rate == rate) + return mtk_dai_pcm_rates[i].reg_value; + + return -EINVAL; +} + +static const struct snd_kcontrol_new mtk_dai_pcm_o000_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN0, 0, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN0_2, 6, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_pcm_o001_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN1, 1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN1_2, 7, 1, 0), +}; + +static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = { + SND_SOC_DAPM_MIXER("I002", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I003", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("O000", SND_SOC_NOPM, 0, 0, + mtk_dai_pcm_o000_mix, + ARRAY_SIZE(mtk_dai_pcm_o000_mix)), + SND_SOC_DAPM_MIXER("O001", SND_SOC_NOPM, 0, 0, + mtk_dai_pcm_o001_mix, + ARRAY_SIZE(mtk_dai_pcm_o001_mix)), + + SND_SOC_DAPM_INPUT("PCM1_INPUT"), + SND_SOC_DAPM_OUTPUT("PCM1_OUTPUT"), +}; + +static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = { + {"I002", NULL, "PCM1 Capture"}, + {"I003", NULL, "PCM1 Capture"}, + + {"O000", "I000 Switch", "I000"}, + {"O001", "I001 Switch", "I001"}, + + {"O000", "I070 Switch", "I070"}, + {"O001", "I071 Switch", "I071"}, + + {"PCM1 Playback", NULL, "O000"}, + {"PCM1 Playback", NULL, "O001"}, + + {"PCM1_OUTPUT", NULL, "PCM1 Playback"}, + {"PCM1 Capture", NULL, "PCM1_INPUT"}, +}; + +static void mtk_dai_pcm_enable(struct mtk_base_afe *afe) +{ + regmap_update_bits(afe->regmap, PCM_INTF_CON1, + PCM_INTF_CON1_PCM_EN, PCM_INTF_CON1_PCM_EN); +} + +static void mtk_dai_pcm_disable(struct mtk_base_afe *afe) +{ + regmap_update_bits(afe->regmap, PCM_INTF_CON1, + PCM_INTF_CON1_PCM_EN, 0x0); +} + +static int mtk_dai_pcm_configure(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_pcm_runtime * const runtime = substream->runtime; + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt8195_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_pcmif_priv *pcmif_priv = afe_priv->dai_priv[dai->id]; + unsigned int slave_mode = pcmif_priv->slave_mode; + unsigned int lrck_inv = pcmif_priv->lrck_inv; + unsigned int bck_inv = pcmif_priv->bck_inv; + unsigned int fmt = pcmif_priv->format; + unsigned int bit_width = dai->sample_bits; + unsigned int val = 0; + unsigned int mask = 0; + int fs = 0; + int mode = 0; + + /* sync freq mode */ + fs = mt8195_afe_fs_timing(runtime->rate); + if (fs < 0) + return -EINVAL; + val |= PCM_INTF_CON2_SYNC_FREQ_MODE(fs); + mask |= PCM_INTF_CON2_SYNC_FREQ_MODE_MASK; + + /* clk domain sel */ + if (runtime->rate % 8000) + val |= PCM_INTF_CON2_CLK_DOMAIN_SEL(MTK_DAI_PCM_CLK_26M_441K); + else + val |= PCM_INTF_CON2_CLK_DOMAIN_SEL(MTK_DAI_PCM_CLK_26M_48K); + mask |= PCM_INTF_CON2_CLK_DOMAIN_SEL_MASK; + + regmap_update_bits(afe->regmap, PCM_INTF_CON2, mask, val); + + val = 0; + mask = 0; + + /* pcm mode */ + mode = mtk_dai_pcm_mode(runtime->rate); + if (mode < 0) + return -EINVAL; + val |= PCM_INTF_CON1_PCM_MODE(mode); + mask |= PCM_INTF_CON1_PCM_MODE_MASK; + + /* pcm format */ + val |= PCM_INTF_CON1_PCM_FMT(fmt); + mask |= PCM_INTF_CON1_PCM_FMT_MASK; + + /* pcm sync length */ + if (fmt == MTK_DAI_PCM_FMT_MODEA || + fmt == MTK_DAI_PCM_FMT_MODEB) + val |= PCM_INTF_CON1_SYNC_LENGTH(1); + else + val |= PCM_INTF_CON1_SYNC_LENGTH(bit_width); + mask |= PCM_INTF_CON1_SYNC_LENGTH_MASK; + + /* pcm bits, word length */ + if (bit_width > 16) { + val |= PCM_INTF_CON1_PCM_24BIT; + val |= PCM_INTF_CON1_PCM_WLEN_64BCK; + } else { + val |= PCM_INTF_CON1_PCM_16BIT; + val |= PCM_INTF_CON1_PCM_WLEN_32BCK; + } + mask |= PCM_INTF_CON1_PCM_BIT_MASK; + mask |= PCM_INTF_CON1_PCM_WLEN_MASK; + + /* master/slave */ + if (!slave_mode) { + val |= PCM_INTF_CON1_PCM_MASTER; + + if (lrck_inv) + val |= PCM_INTF_CON1_SYNC_OUT_INV; + if (bck_inv) + val |= PCM_INTF_CON1_BCLK_OUT_INV; + mask |= PCM_INTF_CON1_CLK_OUT_INV_MASK; + } else { + val |= PCM_INTF_CON1_PCM_SLAVE; + + if (lrck_inv) + val |= PCM_INTF_CON1_SYNC_IN_INV; + if (bck_inv) + val |= PCM_INTF_CON1_BCLK_IN_INV; + mask |= PCM_INTF_CON1_CLK_IN_INV_MASK; + + /* TODO: add asrc setting for slave mode */ + } + mask |= PCM_INTF_CON1_PCM_M_S_MASK; + + regmap_update_bits(afe->regmap, PCM_INTF_CON1, mask, val); + + return 0; +} + +/* dai ops */ +static int mtk_dai_pcm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt8195_afe_private *afe_priv = afe->platform_priv; + + if (dai->component->active) + return 0; + + mt8195_afe_enable_main_clock(afe); + + mt8195_afe_enable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_ASRC11]); + mt8195_afe_enable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_ASRC12]); + mt8195_afe_enable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_PCMIF]); + + return 0; +} + +static void mtk_dai_pcm_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt8195_afe_private *afe_priv = afe->platform_priv; + + if (dai->component->active) + return; + + mtk_dai_pcm_disable(afe); + + mt8195_afe_disable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_PCMIF]); + mt8195_afe_disable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_ASRC12]); + mt8195_afe_disable_clk(afe, afe_priv->clk[MT8195_CLK_AUD_ASRC11]); + + mt8195_afe_disable_main_clock(afe); +} + +static int mtk_dai_pcm_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + int ret = 0; + + if (snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_PLAYBACK) && + snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_CAPTURE)) + return 0; + + ret = mtk_dai_pcm_configure(substream, dai); + if (ret) + return ret; + + mtk_dai_pcm_enable(afe); + + return 0; +} + +static int mtk_dai_pcm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt8195_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_pcmif_priv *pcmif_priv = afe_priv->dai_priv[dai->id]; + + dev_dbg(dai->dev, "%s fmt 0x%x\n", __func__, fmt); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + pcmif_priv->format = MTK_DAI_PCM_FMT_I2S; + break; + case SND_SOC_DAIFMT_DSP_A: + pcmif_priv->format = MTK_DAI_PCM_FMT_MODEA; + break; + case SND_SOC_DAIFMT_DSP_B: + pcmif_priv->format = MTK_DAI_PCM_FMT_MODEB; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + pcmif_priv->bck_inv = 0; + pcmif_priv->lrck_inv = 0; + break; + case SND_SOC_DAIFMT_NB_IF: + pcmif_priv->bck_inv = 0; + pcmif_priv->lrck_inv = 1; + break; + case SND_SOC_DAIFMT_IB_NF: + pcmif_priv->bck_inv = 1; + pcmif_priv->lrck_inv = 0; + break; + case SND_SOC_DAIFMT_IB_IF: + pcmif_priv->bck_inv = 1; + pcmif_priv->lrck_inv = 1; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + pcmif_priv->slave_mode = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + pcmif_priv->slave_mode = 0; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops mtk_dai_pcm_ops = { + .startup = mtk_dai_pcm_startup, + .shutdown = mtk_dai_pcm_shutdown, + .prepare = mtk_dai_pcm_prepare, + .set_fmt = mtk_dai_pcm_set_fmt, +}; + +/* dai driver */ +#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000_48000) + +#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver mtk_dai_pcm_driver[] = { + { + .name = "PCM1", + .id = MT8195_AFE_IO_PCM, + .playback = { + .stream_name = "PCM1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .capture = { + .stream_name = "PCM1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_dai_pcm_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, +}; + +static int init_pcmif_priv_data(struct mtk_base_afe *afe) +{ + struct mt8195_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_pcmif_priv *pcmif_priv; + + pcmif_priv = devm_kzalloc(afe->dev, sizeof(struct mtk_dai_pcmif_priv), + GFP_KERNEL); + if (!pcmif_priv) + return -ENOMEM; + + afe_priv->dai_priv[MT8195_AFE_IO_PCM] = pcmif_priv; + return 0; +} + +int mt8195_dai_pcm_register(struct mtk_base_afe *afe) +{ + struct mtk_base_afe_dai *dai; + + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + + list_add(&dai->list, &afe->sub_dais); + + dai->dai_drivers = mtk_dai_pcm_driver; + dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver); + + dai->dapm_widgets = mtk_dai_pcm_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets); + dai->dapm_routes = mtk_dai_pcm_routes; + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes); + + return init_pcmif_priv_data(afe); +} -- 2.18.0