294 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * MediaTek 8365 ALSA SoC Audio DAI PCM Control4 *5 * Copyright (c) 2024 MediaTek Inc.6 * Authors: Jia Zeng <jia.zeng@mediatek.com>7 * Alexandre Mergnat <amergnat@baylibre.com>8 */9 10#include <linux/bitops.h>11#include <linux/regmap.h>12#include <sound/pcm_params.h>13#include "mt8365-afe-clk.h"14#include "mt8365-afe-common.h"15 16struct mt8365_pcm_intf_data {17 bool slave_mode;18 bool lrck_inv;19 bool bck_inv;20 unsigned int format;21};22 23/* DAI Drivers */24 25static void mt8365_dai_enable_pcm1(struct mtk_base_afe *afe)26{27 regmap_update_bits(afe->regmap, PCM_INTF_CON1,28 PCM_INTF_CON1_EN, PCM_INTF_CON1_EN);29}30 31static void mt8365_dai_disable_pcm1(struct mtk_base_afe *afe)32{33 regmap_update_bits(afe->regmap, PCM_INTF_CON1,34 PCM_INTF_CON1_EN, 0x0);35}36 37static int mt8365_dai_configure_pcm1(struct snd_pcm_substream *substream,38 struct snd_soc_dai *dai)39{40 struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);41 struct mt8365_afe_private *afe_priv = afe->platform_priv;42 struct mt8365_pcm_intf_data *pcm_priv = afe_priv->dai_priv[MT8365_AFE_IO_PCM1];43 bool slave_mode = pcm_priv->slave_mode;44 bool lrck_inv = pcm_priv->lrck_inv;45 bool bck_inv = pcm_priv->bck_inv;46 unsigned int fmt = pcm_priv->format;47 unsigned int bit_width = dai->sample_bits;48 unsigned int val = 0;49 50 if (!slave_mode) {51 val |= PCM_INTF_CON1_MASTER_MODE |52 PCM_INTF_CON1_BYPASS_ASRC;53 54 if (lrck_inv)55 val |= PCM_INTF_CON1_SYNC_OUT_INV;56 if (bck_inv)57 val |= PCM_INTF_CON1_BCLK_OUT_INV;58 } else {59 val |= PCM_INTF_CON1_SLAVE_MODE;60 61 if (lrck_inv)62 val |= PCM_INTF_CON1_SYNC_IN_INV;63 if (bck_inv)64 val |= PCM_INTF_CON1_BCLK_IN_INV;65 66 /* TODO: add asrc setting */67 }68 69 val |= FIELD_PREP(PCM_INTF_CON1_FORMAT_MASK, fmt);70 71 if (fmt == MT8365_PCM_FORMAT_PCMA ||72 fmt == MT8365_PCM_FORMAT_PCMB)73 val |= PCM_INTF_CON1_SYNC_LEN(1);74 else75 val |= PCM_INTF_CON1_SYNC_LEN(bit_width);76 77 switch (substream->runtime->rate) {78 case 48000:79 val |= PCM_INTF_CON1_FS_48K;80 break;81 case 32000:82 val |= PCM_INTF_CON1_FS_32K;83 break;84 case 16000:85 val |= PCM_INTF_CON1_FS_16K;86 break;87 case 8000:88 val |= PCM_INTF_CON1_FS_8K;89 break;90 default:91 return -EINVAL;92 }93 94 if (bit_width > 16)95 val |= PCM_INTF_CON1_24BIT | PCM_INTF_CON1_64BCK;96 else97 val |= PCM_INTF_CON1_16BIT | PCM_INTF_CON1_32BCK;98 99 val |= PCM_INTF_CON1_EXT_MODEM;100 101 regmap_update_bits(afe->regmap, PCM_INTF_CON1,102 PCM_INTF_CON1_CONFIG_MASK, val);103 104 return 0;105}106 107static int mt8365_dai_pcm1_startup(struct snd_pcm_substream *substream,108 struct snd_soc_dai *dai)109{110 struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);111 112 if (snd_soc_dai_active(dai))113 return 0;114 115 mt8365_afe_enable_main_clk(afe);116 117 return 0;118}119 120static void mt8365_dai_pcm1_shutdown(struct snd_pcm_substream *substream,121 struct snd_soc_dai *dai)122{123 struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);124 125 if (snd_soc_dai_active(dai))126 return;127 128 mt8365_dai_disable_pcm1(afe);129 mt8365_afe_disable_main_clk(afe);130}131 132static int mt8365_dai_pcm1_prepare(struct snd_pcm_substream *substream,133 struct snd_soc_dai *dai)134{135 struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);136 int ret;137 138 if ((snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_PLAYBACK) +139 snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_CAPTURE)) > 1) {140 dev_info(afe->dev, "%s '%s' active(%u-%u) already\n",141 __func__, snd_pcm_stream_str(substream),142 snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_PLAYBACK),143 snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_CAPTURE));144 return 0;145 }146 147 ret = mt8365_dai_configure_pcm1(substream, dai);148 if (ret)149 return ret;150 151 mt8365_dai_enable_pcm1(afe);152 153 return 0;154}155 156static int mt8365_dai_pcm1_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)157{158 struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);159 struct mt8365_afe_private *afe_priv = afe->platform_priv;160 struct mt8365_pcm_intf_data *pcm_priv = afe_priv->dai_priv[MT8365_AFE_IO_PCM1];161 162 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {163 case SND_SOC_DAIFMT_I2S:164 pcm_priv->format = MT8365_PCM_FORMAT_I2S;165 break;166 default:167 return -EINVAL;168 }169 170 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {171 case SND_SOC_DAIFMT_NB_NF:172 pcm_priv->bck_inv = false;173 pcm_priv->lrck_inv = false;174 break;175 case SND_SOC_DAIFMT_NB_IF:176 pcm_priv->bck_inv = false;177 pcm_priv->lrck_inv = true;178 break;179 case SND_SOC_DAIFMT_IB_NF:180 pcm_priv->bck_inv = true;181 pcm_priv->lrck_inv = false;182 break;183 case SND_SOC_DAIFMT_IB_IF:184 pcm_priv->bck_inv = true;185 pcm_priv->lrck_inv = true;186 break;187 default:188 return -EINVAL;189 }190 191 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {192 case SND_SOC_DAIFMT_CBM_CFM:193 pcm_priv->slave_mode = true;194 break;195 case SND_SOC_DAIFMT_CBS_CFS:196 pcm_priv->slave_mode = false;197 break;198 default:199 return -EINVAL;200 }201 202 return 0;203}204 205static const struct snd_soc_dai_ops mt8365_dai_pcm1_ops = {206 .startup = mt8365_dai_pcm1_startup,207 .shutdown = mt8365_dai_pcm1_shutdown,208 .prepare = mt8365_dai_pcm1_prepare,209 .set_fmt = mt8365_dai_pcm1_set_fmt,210};211 212static struct snd_soc_dai_driver mtk_dai_pcm_driver[] = {213 {214 .name = "PCM1",215 .id = MT8365_AFE_IO_PCM1,216 .playback = {217 .stream_name = "PCM1 Playback",218 .channels_min = 1,219 .channels_max = 2,220 .rates = SNDRV_PCM_RATE_8000 |221 SNDRV_PCM_RATE_16000 |222 SNDRV_PCM_RATE_32000 |223 SNDRV_PCM_RATE_48000,224 .formats = SNDRV_PCM_FMTBIT_S16_LE |225 SNDRV_PCM_FMTBIT_S32_LE,226 },227 .capture = {228 .stream_name = "PCM1 Capture",229 .channels_min = 1,230 .channels_max = 2,231 .rates = SNDRV_PCM_RATE_8000 |232 SNDRV_PCM_RATE_16000 |233 SNDRV_PCM_RATE_32000 |234 SNDRV_PCM_RATE_48000,235 .formats = SNDRV_PCM_FMTBIT_S16_LE |236 SNDRV_PCM_FMTBIT_S32_LE,237 },238 .ops = &mt8365_dai_pcm1_ops,239 .symmetric_rate = 1,240 .symmetric_sample_bits = 1,241 }242};243 244/* DAI widget */245 246static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = {247 SND_SOC_DAPM_OUTPUT("PCM1 Out"),248 SND_SOC_DAPM_INPUT("PCM1 In"),249};250 251/* DAI route */252 253static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = {254 {"PCM1 Playback", NULL, "O07"},255 {"PCM1 Playback", NULL, "O08"},256 {"PCM1 Out", NULL, "PCM1 Playback"},257 258 {"I09", NULL, "PCM1 Capture"},259 {"I22", NULL, "PCM1 Capture"},260 {"PCM1 Capture", NULL, "PCM1 In"},261};262 263static int init_pcmif_priv_data(struct mtk_base_afe *afe)264{265 struct mt8365_afe_private *afe_priv = afe->platform_priv;266 struct mt8365_pcm_intf_data *pcmif_priv;267 268 pcmif_priv = devm_kzalloc(afe->dev, sizeof(struct mt8365_pcm_intf_data),269 GFP_KERNEL);270 if (!pcmif_priv)271 return -ENOMEM;272 273 afe_priv->dai_priv[MT8365_AFE_IO_PCM1] = pcmif_priv;274 return 0;275}276 277int mt8365_dai_pcm_register(struct mtk_base_afe *afe)278{279 struct mtk_base_afe_dai *dai;280 281 dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);282 if (!dai)283 return -ENOMEM;284 285 list_add(&dai->list, &afe->sub_dais);286 dai->dai_drivers = mtk_dai_pcm_driver;287 dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver);288 dai->dapm_widgets = mtk_dai_pcm_widgets;289 dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets);290 dai->dapm_routes = mtk_dai_pcm_routes;291 dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes);292 return init_pcmif_priv_data(afe);293}294