lkml.org 
[lkml]   [2024]   [Feb]   [26]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Date
    Subject[PATCH 09/18] ASoC: mediatek: mt8365: Add PCM DAI support
    Add Pulse Code Modulation Device Audio Interface support for MT8365 SoC.

    Signed-off-by: Alexandre Mergnat <amergnat@baylibre.com>
    ---
    sound/soc/mediatek/mt8365/mt8365-dai-pcm.c | 298 +++++++++++++++++++++++++++++
    1 file changed, 298 insertions(+)

    diff --git a/sound/soc/mediatek/mt8365/mt8365-dai-pcm.c b/sound/soc/mediatek/mt8365/mt8365-dai-pcm.c
    new file mode 100644
    index 000000000000..02ee81663e50
    --- /dev/null
    +++ b/sound/soc/mediatek/mt8365/mt8365-dai-pcm.c
    @@ -0,0 +1,298 @@
    +// SPDX-License-Identifier: GPL-2.0
    +/*
    + * Mediatek 8365 ALSA SoC Audio DAI PCM Control
    + *
    + * Copyright (c) 2024 MediaTek Inc.
    + * Authors: Jia Zeng <jia.zeng@mediatek.com>
    + * Alexandre Mergnat <amergnat@baylibre.com>
    + */
    +
    +#include <linux/bitops.h>
    +#include <linux/regmap.h>
    +#include <sound/pcm_params.h>
    +#include "mt8365-afe-clk.h"
    +#include "mt8365-afe-common.h"
    +
    +struct mt8365_pcm_intf_data {
    + bool slave_mode;
    + bool lrck_inv;
    + bool bck_inv;
    + unsigned int format;
    +};
    +
    +/* DAI Drivers */
    +
    +static void mt8365_dai_enable_pcm1(struct mtk_base_afe *afe)
    +{
    + regmap_update_bits(afe->regmap, PCM_INTF_CON1,
    + PCM_INTF_CON1_EN, PCM_INTF_CON1_EN);
    +}
    +
    +static void mt8365_dai_disable_pcm1(struct mtk_base_afe *afe)
    +{
    + regmap_update_bits(afe->regmap, PCM_INTF_CON1,
    + PCM_INTF_CON1_EN, 0x0);
    +}
    +
    +static int mt8365_dai_configure_pcm1(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 mt8365_afe_private *afe_priv = afe->platform_priv;
    + struct mt8365_pcm_intf_data *pcm_priv = afe_priv->dai_priv[MT8365_AFE_IO_PCM1];
    + bool slave_mode = pcm_priv->slave_mode;
    + bool lrck_inv = pcm_priv->lrck_inv;
    + bool bck_inv = pcm_priv->bck_inv;
    + unsigned int fmt = pcm_priv->format;
    + unsigned int bit_width = dai->sample_bits;
    + unsigned int val = 0;
    +
    + if (!slave_mode) {
    + val |= PCM_INTF_CON1_MASTER_MODE |
    + PCM_INTF_CON1_BYPASS_ASRC;
    +
    + if (lrck_inv)
    + val |= PCM_INTF_CON1_SYNC_OUT_INV;
    + if (bck_inv)
    + val |= PCM_INTF_CON1_BCLK_OUT_INV;
    + } else {
    + val |= PCM_INTF_CON1_SLAVE_MODE;
    +
    + if (lrck_inv)
    + val |= PCM_INTF_CON1_SYNC_IN_INV;
    + if (bck_inv)
    + val |= PCM_INTF_CON1_BCLK_IN_INV;
    +
    + // TODO: add asrc setting
    + }
    +
    + val |= FIELD_PREP(PCM_INTF_CON1_FORMAT_MASK, fmt);
    +
    + if (fmt == MT8365_PCM_FORMAT_PCMA ||
    + fmt == MT8365_PCM_FORMAT_PCMB)
    + val |= PCM_INTF_CON1_SYNC_LEN(1);
    + else
    + val |= PCM_INTF_CON1_SYNC_LEN(bit_width);
    +
    + switch (runtime->rate) {
    + case 48000:
    + val |= PCM_INTF_CON1_FS_48K;
    + break;
    + case 32000:
    + val |= PCM_INTF_CON1_FS_32K;
    + break;
    + case 16000:
    + val |= PCM_INTF_CON1_FS_16K;
    + break;
    + case 8000:
    + val |= PCM_INTF_CON1_FS_8K;
    + break;
    + default:
    + return -EINVAL;
    + }
    +
    + if (bit_width > 16)
    + val |= PCM_INTF_CON1_24BIT | PCM_INTF_CON1_64BCK;
    + else
    + val |= PCM_INTF_CON1_16BIT | PCM_INTF_CON1_32BCK;
    +
    + val |= PCM_INTF_CON1_EXT_MODEM;
    +
    + regmap_update_bits(afe->regmap, PCM_INTF_CON1,
    + PCM_INTF_CON1_CONFIG_MASK, val);
    +
    + return 0;
    +}
    +
    +static int mt8365_dai_pcm1_startup(struct snd_pcm_substream *substream,
    + struct snd_soc_dai *dai)
    +{
    + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
    +
    + if (snd_soc_dai_active(dai))
    + return 0;
    +
    + mt8365_afe_enable_main_clk(afe);
    +
    + return 0;
    +}
    +
    +static void mt8365_dai_pcm1_shutdown(struct snd_pcm_substream *substream,
    + struct snd_soc_dai *dai)
    +{
    + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
    +
    + if (snd_soc_dai_active(dai))
    + return;
    +
    + mt8365_dai_disable_pcm1(afe);
    + mt8365_afe_disable_main_clk(afe);
    +}
    +
    +static int mt8365_dai_pcm1_prepare(struct snd_pcm_substream *substream,
    + struct snd_soc_dai *dai)
    +{
    + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
    + int ret;
    +
    + if ((snd_soc_dai_stream_active_playback(dai) + snd_soc_dai_stream_active_capture(dai))
    + > 1) {
    + dev_info(afe->dev, "%s '%s' active(%u-%u) already\n",
    + __func__, snd_pcm_stream_str(substream),
    + snd_soc_dai_stream_active_playback(dai),
    + snd_soc_dai_stream_active_capture(dai));
    + return 0;
    + }
    +
    + ret = mt8365_dai_configure_pcm1(substream, dai);
    + if (ret)
    + return ret;
    +
    + mt8365_dai_enable_pcm1(afe);
    +
    + return 0;
    +}
    +
    +static int mt8365_dai_pcm1_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
    +{
    + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
    + struct mt8365_afe_private *afe_priv = afe->platform_priv;
    + struct mt8365_pcm_intf_data *pcm_priv = afe_priv->dai_priv[MT8365_AFE_IO_PCM1];
    +
    + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
    + case SND_SOC_DAIFMT_I2S:
    + pcm_priv->format = MT8365_PCM_FORMAT_I2S;
    + break;
    + default:
    + return -EINVAL;
    + }
    +
    + switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
    + case SND_SOC_DAIFMT_NB_NF:
    + pcm_priv->bck_inv = false;
    + pcm_priv->lrck_inv = false;
    + break;
    + case SND_SOC_DAIFMT_NB_IF:
    + pcm_priv->bck_inv = false;
    + pcm_priv->lrck_inv = true;
    + break;
    + case SND_SOC_DAIFMT_IB_NF:
    + pcm_priv->bck_inv = true;
    + pcm_priv->lrck_inv = false;
    + break;
    + case SND_SOC_DAIFMT_IB_IF:
    + pcm_priv->bck_inv = true;
    + pcm_priv->lrck_inv = true;
    + break;
    + default:
    + return -EINVAL;
    + }
    +
    + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
    + case SND_SOC_DAIFMT_CBM_CFM:
    + pcm_priv->slave_mode = true;
    + break;
    + case SND_SOC_DAIFMT_CBS_CFS:
    + pcm_priv->slave_mode = false;
    + break;
    + default:
    + return -EINVAL;
    + }
    +
    + return 0;
    +}
    +
    +static const struct snd_soc_dai_ops mt8365_dai_pcm1_ops = {
    + .startup = mt8365_dai_pcm1_startup,
    + .shutdown = mt8365_dai_pcm1_shutdown,
    + .prepare = mt8365_dai_pcm1_prepare,
    + .set_fmt = mt8365_dai_pcm1_set_fmt,
    +};
    +
    +static struct snd_soc_dai_driver mtk_dai_pcm_driver[] = {
    + {
    + .name = "PCM1",
    + .id = MT8365_AFE_IO_PCM1,
    + .playback = {
    + .stream_name = "PCM1 Playback",
    + .channels_min = 1,
    + .channels_max = 2,
    + .rates = SNDRV_PCM_RATE_8000 |
    + SNDRV_PCM_RATE_16000 |
    + SNDRV_PCM_RATE_32000 |
    + SNDRV_PCM_RATE_48000,
    + .formats = SNDRV_PCM_FMTBIT_S16_LE |
    + SNDRV_PCM_FMTBIT_S32_LE,
    + },
    + .capture = {
    + .stream_name = "PCM1 Capture",
    + .channels_min = 1,
    + .channels_max = 2,
    + .rates = SNDRV_PCM_RATE_8000 |
    + SNDRV_PCM_RATE_16000 |
    + SNDRV_PCM_RATE_32000 |
    + SNDRV_PCM_RATE_48000,
    + .formats = SNDRV_PCM_FMTBIT_S16_LE |
    + SNDRV_PCM_FMTBIT_S32_LE,
    + },
    + .ops = &mt8365_dai_pcm1_ops,
    + .symmetric_rate = 1,
    + .symmetric_sample_bits = 1,
    + }
    +};
    +
    +/* DAI widget */
    +
    +static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = {
    + SND_SOC_DAPM_OUTPUT("PCM1 Out"),
    + SND_SOC_DAPM_INPUT("PCM1 In"),
    +};
    +
    +/* DAI route */
    +
    +static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = {
    + {"PCM1 Playback", NULL, "O07"},
    + {"PCM1 Playback", NULL, "O08"},
    + {"PCM1 Out", NULL, "PCM1 Playback"},
    +
    + {"I09", NULL, "PCM1 Capture"},
    + {"I22", NULL, "PCM1 Capture"},
    + {"PCM1 Capture", NULL, "PCM1 In"},
    +};
    +
    +static int init_pcmif_priv_data(struct mtk_base_afe *afe)
    +{
    + struct mt8365_afe_private *afe_priv = afe->platform_priv;
    + struct mt8365_pcm_intf_data *pcmif_priv;
    +
    + pcmif_priv = devm_kzalloc(afe->dev, sizeof(struct mt8365_pcm_intf_data),
    + GFP_KERNEL);
    + if (!pcmif_priv)
    + return -ENOMEM;
    +
    + afe_priv->dai_priv[MT8365_AFE_IO_PCM1] = pcmif_priv;
    + return 0;
    +}
    +
    +int mt8365_dai_pcm_register(struct mtk_base_afe *afe)
    +{
    + struct mtk_base_afe_dai *dai;
    +
    + dev_dbg(afe->dev, "%s()\n", __func__);
    +
    + 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.25.1


    \
     
     \ /
      Last update: 2024-05-27 15:22    [W:4.342 / U:0.088 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site