Path: blob/master/sound/soc/mediatek/mt8365/mt8365-dai-pcm.c
26488 views
// SPDX-License-Identifier: GPL-2.01/*2* MediaTek 8365 ALSA SoC Audio DAI PCM Control3*4* Copyright (c) 2024 MediaTek Inc.5* Authors: Jia Zeng <[email protected]>6* Alexandre Mergnat <[email protected]>7*/89#include <linux/bitops.h>10#include <linux/regmap.h>11#include <sound/pcm_params.h>12#include "mt8365-afe-clk.h"13#include "mt8365-afe-common.h"1415struct mt8365_pcm_intf_data {16bool slave_mode;17bool lrck_inv;18bool bck_inv;19unsigned int format;20};2122/* DAI Drivers */2324static void mt8365_dai_enable_pcm1(struct mtk_base_afe *afe)25{26regmap_update_bits(afe->regmap, PCM_INTF_CON1,27PCM_INTF_CON1_EN, PCM_INTF_CON1_EN);28}2930static void mt8365_dai_disable_pcm1(struct mtk_base_afe *afe)31{32regmap_update_bits(afe->regmap, PCM_INTF_CON1,33PCM_INTF_CON1_EN, 0x0);34}3536static int mt8365_dai_configure_pcm1(struct snd_pcm_substream *substream,37struct snd_soc_dai *dai)38{39struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);40struct mt8365_afe_private *afe_priv = afe->platform_priv;41struct mt8365_pcm_intf_data *pcm_priv = afe_priv->dai_priv[MT8365_AFE_IO_PCM1];42bool slave_mode = pcm_priv->slave_mode;43bool lrck_inv = pcm_priv->lrck_inv;44bool bck_inv = pcm_priv->bck_inv;45unsigned int fmt = pcm_priv->format;46unsigned int bit_width = dai->symmetric_sample_bits;47unsigned int val = 0;4849if (!slave_mode) {50val |= PCM_INTF_CON1_MASTER_MODE |51PCM_INTF_CON1_BYPASS_ASRC;5253if (lrck_inv)54val |= PCM_INTF_CON1_SYNC_OUT_INV;55if (bck_inv)56val |= PCM_INTF_CON1_BCLK_OUT_INV;57} else {58val |= PCM_INTF_CON1_SLAVE_MODE;5960if (lrck_inv)61val |= PCM_INTF_CON1_SYNC_IN_INV;62if (bck_inv)63val |= PCM_INTF_CON1_BCLK_IN_INV;6465/* TODO: add asrc setting */66}6768val |= FIELD_PREP(PCM_INTF_CON1_FORMAT_MASK, fmt);6970if (fmt == MT8365_PCM_FORMAT_PCMA ||71fmt == MT8365_PCM_FORMAT_PCMB)72val |= PCM_INTF_CON1_SYNC_LEN(1);73else74val |= PCM_INTF_CON1_SYNC_LEN(bit_width);7576switch (substream->runtime->rate) {77case 48000:78val |= PCM_INTF_CON1_FS_48K;79break;80case 32000:81val |= PCM_INTF_CON1_FS_32K;82break;83case 16000:84val |= PCM_INTF_CON1_FS_16K;85break;86case 8000:87val |= PCM_INTF_CON1_FS_8K;88break;89default:90return -EINVAL;91}9293if (bit_width > 16)94val |= PCM_INTF_CON1_24BIT | PCM_INTF_CON1_64BCK;95else96val |= PCM_INTF_CON1_16BIT | PCM_INTF_CON1_32BCK;9798val |= PCM_INTF_CON1_EXT_MODEM;99100regmap_update_bits(afe->regmap, PCM_INTF_CON1,101PCM_INTF_CON1_CONFIG_MASK, val);102103return 0;104}105106static int mt8365_dai_pcm1_startup(struct snd_pcm_substream *substream,107struct snd_soc_dai *dai)108{109struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);110111if (snd_soc_dai_active(dai))112return 0;113114mt8365_afe_enable_main_clk(afe);115116return 0;117}118119static void mt8365_dai_pcm1_shutdown(struct snd_pcm_substream *substream,120struct snd_soc_dai *dai)121{122struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);123124if (snd_soc_dai_active(dai))125return;126127mt8365_dai_disable_pcm1(afe);128mt8365_afe_disable_main_clk(afe);129}130131static int mt8365_dai_pcm1_prepare(struct snd_pcm_substream *substream,132struct snd_soc_dai *dai)133{134struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);135int ret;136137if ((snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_PLAYBACK) +138snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_CAPTURE)) > 1) {139dev_info(afe->dev, "%s '%s' active(%u-%u) already\n",140__func__, snd_pcm_stream_str(substream),141snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_PLAYBACK),142snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_CAPTURE));143return 0;144}145146ret = mt8365_dai_configure_pcm1(substream, dai);147if (ret)148return ret;149150mt8365_dai_enable_pcm1(afe);151152return 0;153}154155static int mt8365_dai_pcm1_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)156{157struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);158struct mt8365_afe_private *afe_priv = afe->platform_priv;159struct mt8365_pcm_intf_data *pcm_priv = afe_priv->dai_priv[MT8365_AFE_IO_PCM1];160161switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {162case SND_SOC_DAIFMT_I2S:163pcm_priv->format = MT8365_PCM_FORMAT_I2S;164break;165default:166return -EINVAL;167}168169switch (fmt & SND_SOC_DAIFMT_INV_MASK) {170case SND_SOC_DAIFMT_NB_NF:171pcm_priv->bck_inv = false;172pcm_priv->lrck_inv = false;173break;174case SND_SOC_DAIFMT_NB_IF:175pcm_priv->bck_inv = false;176pcm_priv->lrck_inv = true;177break;178case SND_SOC_DAIFMT_IB_NF:179pcm_priv->bck_inv = true;180pcm_priv->lrck_inv = false;181break;182case SND_SOC_DAIFMT_IB_IF:183pcm_priv->bck_inv = true;184pcm_priv->lrck_inv = true;185break;186default:187return -EINVAL;188}189190switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {191case SND_SOC_DAIFMT_CBP_CFP:192pcm_priv->slave_mode = true;193break;194case SND_SOC_DAIFMT_CBC_CFC:195pcm_priv->slave_mode = false;196break;197default:198return -EINVAL;199}200201return 0;202}203204static const struct snd_soc_dai_ops mt8365_dai_pcm1_ops = {205.startup = mt8365_dai_pcm1_startup,206.shutdown = mt8365_dai_pcm1_shutdown,207.prepare = mt8365_dai_pcm1_prepare,208.set_fmt = mt8365_dai_pcm1_set_fmt,209};210211static struct snd_soc_dai_driver mtk_dai_pcm_driver[] = {212{213.name = "PCM1",214.id = MT8365_AFE_IO_PCM1,215.playback = {216.stream_name = "PCM1 Playback",217.channels_min = 1,218.channels_max = 2,219.rates = SNDRV_PCM_RATE_8000 |220SNDRV_PCM_RATE_16000 |221SNDRV_PCM_RATE_32000 |222SNDRV_PCM_RATE_48000,223.formats = SNDRV_PCM_FMTBIT_S16_LE |224SNDRV_PCM_FMTBIT_S32_LE,225},226.capture = {227.stream_name = "PCM1 Capture",228.channels_min = 1,229.channels_max = 2,230.rates = SNDRV_PCM_RATE_8000 |231SNDRV_PCM_RATE_16000 |232SNDRV_PCM_RATE_32000 |233SNDRV_PCM_RATE_48000,234.formats = SNDRV_PCM_FMTBIT_S16_LE |235SNDRV_PCM_FMTBIT_S32_LE,236},237.ops = &mt8365_dai_pcm1_ops,238.symmetric_rate = 1,239.symmetric_sample_bits = 1,240}241};242243/* DAI widget */244245static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = {246SND_SOC_DAPM_OUTPUT("PCM1 Out"),247SND_SOC_DAPM_INPUT("PCM1 In"),248};249250/* DAI route */251252static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = {253{"PCM1 Playback", NULL, "O07"},254{"PCM1 Playback", NULL, "O08"},255{"PCM1 Out", NULL, "PCM1 Playback"},256257{"I09", NULL, "PCM1 Capture"},258{"I22", NULL, "PCM1 Capture"},259{"PCM1 Capture", NULL, "PCM1 In"},260};261262static int init_pcmif_priv_data(struct mtk_base_afe *afe)263{264struct mt8365_afe_private *afe_priv = afe->platform_priv;265struct mt8365_pcm_intf_data *pcmif_priv;266267pcmif_priv = devm_kzalloc(afe->dev, sizeof(struct mt8365_pcm_intf_data),268GFP_KERNEL);269if (!pcmif_priv)270return -ENOMEM;271272afe_priv->dai_priv[MT8365_AFE_IO_PCM1] = pcmif_priv;273return 0;274}275276int mt8365_dai_pcm_register(struct mtk_base_afe *afe)277{278struct mtk_base_afe_dai *dai;279280dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);281if (!dai)282return -ENOMEM;283284list_add(&dai->list, &afe->sub_dais);285dai->dai_drivers = mtk_dai_pcm_driver;286dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver);287dai->dapm_widgets = mtk_dai_pcm_widgets;288dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets);289dai->dapm_routes = mtk_dai_pcm_routes;290dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes);291return init_pcmif_priv_data(afe);292}293294295