417 lines · c
1// SPDX-License-Identifier: GPL-2.0+2//3// AMD ALSA SoC PCM Driver4//5// Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.6 7#include <linux/platform_device.h>8#include <linux/module.h>9#include <linux/err.h>10#include <linux/io.h>11#include <sound/pcm_params.h>12#include <sound/soc.h>13#include <sound/soc-dai.h>14#include <linux/dma-mapping.h>15 16#include "acp5x.h"17 18#define DRV_NAME "acp5x_i2s_playcap"19 20static int acp5x_i2s_set_fmt(struct snd_soc_dai *cpu_dai,21 unsigned int fmt)22{23 struct i2s_dev_data *adata;24 int mode;25 26 adata = snd_soc_dai_get_drvdata(cpu_dai);27 mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;28 switch (mode) {29 case SND_SOC_DAIFMT_I2S:30 adata->tdm_mode = TDM_DISABLE;31 break;32 case SND_SOC_DAIFMT_DSP_A:33 adata->tdm_mode = TDM_ENABLE;34 break;35 default:36 return -EINVAL;37 }38 mode = fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;39 switch (mode) {40 case SND_SOC_DAIFMT_BP_FP:41 adata->master_mode = I2S_MASTER_MODE_ENABLE;42 break;43 case SND_SOC_DAIFMT_BC_FC:44 adata->master_mode = I2S_MASTER_MODE_DISABLE;45 break;46 }47 return 0;48}49 50static int acp5x_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai,51 u32 tx_mask, u32 rx_mask,52 int slots, int slot_width)53{54 struct i2s_dev_data *adata;55 u32 frm_len;56 u16 slot_len;57 58 adata = snd_soc_dai_get_drvdata(cpu_dai);59 60 /* These values are as per Hardware Spec */61 switch (slot_width) {62 case SLOT_WIDTH_8:63 slot_len = 8;64 break;65 case SLOT_WIDTH_16:66 slot_len = 16;67 break;68 case SLOT_WIDTH_24:69 slot_len = 24;70 break;71 case SLOT_WIDTH_32:72 slot_len = 0;73 break;74 default:75 return -EINVAL;76 }77 frm_len = FRM_LEN | (slots << 15) | (slot_len << 18);78 adata->tdm_fmt = frm_len;79 return 0;80}81 82static int acp5x_i2s_hwparams(struct snd_pcm_substream *substream,83 struct snd_pcm_hw_params *params,84 struct snd_soc_dai *dai)85{86 struct i2s_stream_instance *rtd;87 struct snd_soc_pcm_runtime *prtd;88 struct snd_soc_card *card;89 struct acp5x_platform_info *pinfo;90 struct i2s_dev_data *adata;91 92 u32 val;93 u32 reg_val, frmt_reg;94 u32 lrclk_div_val, bclk_div_val;95 96 lrclk_div_val = 0;97 bclk_div_val = 0;98 prtd = snd_soc_substream_to_rtd(substream);99 rtd = substream->runtime->private_data;100 card = prtd->card;101 adata = snd_soc_dai_get_drvdata(dai);102 pinfo = snd_soc_card_get_drvdata(card);103 if (pinfo) {104 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)105 rtd->i2s_instance = pinfo->play_i2s_instance;106 else107 rtd->i2s_instance = pinfo->cap_i2s_instance;108 }109 110 /* These values are as per Hardware Spec */111 switch (params_format(params)) {112 case SNDRV_PCM_FORMAT_U8:113 case SNDRV_PCM_FORMAT_S8:114 rtd->xfer_resolution = 0x0;115 break;116 case SNDRV_PCM_FORMAT_S16_LE:117 rtd->xfer_resolution = 0x02;118 break;119 case SNDRV_PCM_FORMAT_S24_LE:120 rtd->xfer_resolution = 0x04;121 break;122 case SNDRV_PCM_FORMAT_S32_LE:123 rtd->xfer_resolution = 0x05;124 break;125 default:126 return -EINVAL;127 }128 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {129 switch (rtd->i2s_instance) {130 case I2S_HS_INSTANCE:131 reg_val = ACP_HSTDM_ITER;132 frmt_reg = ACP_HSTDM_TXFRMT;133 break;134 case I2S_SP_INSTANCE:135 default:136 reg_val = ACP_I2STDM_ITER;137 frmt_reg = ACP_I2STDM_TXFRMT;138 }139 } else {140 switch (rtd->i2s_instance) {141 case I2S_HS_INSTANCE:142 reg_val = ACP_HSTDM_IRER;143 frmt_reg = ACP_HSTDM_RXFRMT;144 break;145 case I2S_SP_INSTANCE:146 default:147 reg_val = ACP_I2STDM_IRER;148 frmt_reg = ACP_I2STDM_RXFRMT;149 }150 }151 if (adata->tdm_mode) {152 val = acp_readl(rtd->acp5x_base + reg_val);153 acp_writel(val | 0x2, rtd->acp5x_base + reg_val);154 acp_writel(adata->tdm_fmt, rtd->acp5x_base + frmt_reg);155 }156 val = acp_readl(rtd->acp5x_base + reg_val);157 val &= ~ACP5x_ITER_IRER_SAMP_LEN_MASK;158 val = val | (rtd->xfer_resolution << 3);159 acp_writel(val, rtd->acp5x_base + reg_val);160 161 if (adata->master_mode) {162 switch (params_format(params)) {163 case SNDRV_PCM_FORMAT_S16_LE:164 switch (params_rate(params)) {165 case 8000:166 bclk_div_val = 768;167 break;168 case 16000:169 bclk_div_val = 384;170 break;171 case 24000:172 bclk_div_val = 256;173 break;174 case 32000:175 bclk_div_val = 192;176 break;177 case 44100:178 case 48000:179 bclk_div_val = 128;180 break;181 case 88200:182 case 96000:183 bclk_div_val = 64;184 break;185 case 192000:186 bclk_div_val = 32;187 break;188 default:189 return -EINVAL;190 }191 lrclk_div_val = 32;192 break;193 case SNDRV_PCM_FORMAT_S32_LE:194 switch (params_rate(params)) {195 case 8000:196 bclk_div_val = 384;197 break;198 case 16000:199 bclk_div_val = 192;200 break;201 case 24000:202 bclk_div_val = 128;203 break;204 case 32000:205 bclk_div_val = 96;206 break;207 case 44100:208 case 48000:209 bclk_div_val = 64;210 break;211 case 88200:212 case 96000:213 bclk_div_val = 32;214 break;215 case 192000:216 bclk_div_val = 16;217 break;218 default:219 return -EINVAL;220 }221 lrclk_div_val = 64;222 break;223 default:224 return -EINVAL;225 }226 rtd->lrclk_div = lrclk_div_val;227 rtd->bclk_div = bclk_div_val;228 }229 return 0;230}231 232static int acp5x_i2s_trigger(struct snd_pcm_substream *substream,233 int cmd, struct snd_soc_dai *dai)234{235 struct i2s_stream_instance *rtd;236 struct i2s_dev_data *adata;237 u32 ret, val, period_bytes, reg_val, ier_val, water_val;238 u32 buf_size, buf_reg;239 240 adata = snd_soc_dai_get_drvdata(dai);241 rtd = substream->runtime->private_data;242 period_bytes = frames_to_bytes(substream->runtime,243 substream->runtime->period_size);244 buf_size = frames_to_bytes(substream->runtime,245 substream->runtime->buffer_size);246 switch (cmd) {247 case SNDRV_PCM_TRIGGER_START:248 case SNDRV_PCM_TRIGGER_RESUME:249 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:250 rtd->bytescount = acp_get_byte_count(rtd,251 substream->stream);252 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {253 switch (rtd->i2s_instance) {254 case I2S_HS_INSTANCE:255 water_val =256 ACP_HS_TX_INTR_WATERMARK_SIZE;257 reg_val = ACP_HSTDM_ITER;258 ier_val = ACP_HSTDM_IER;259 buf_reg = ACP_HS_TX_RINGBUFSIZE;260 break;261 case I2S_SP_INSTANCE:262 default:263 water_val =264 ACP_I2S_TX_INTR_WATERMARK_SIZE;265 reg_val = ACP_I2STDM_ITER;266 ier_val = ACP_I2STDM_IER;267 buf_reg = ACP_I2S_TX_RINGBUFSIZE;268 }269 } else {270 switch (rtd->i2s_instance) {271 case I2S_HS_INSTANCE:272 water_val =273 ACP_HS_RX_INTR_WATERMARK_SIZE;274 reg_val = ACP_HSTDM_IRER;275 ier_val = ACP_HSTDM_IER;276 buf_reg = ACP_HS_RX_RINGBUFSIZE;277 break;278 case I2S_SP_INSTANCE:279 default:280 water_val =281 ACP_I2S_RX_INTR_WATERMARK_SIZE;282 reg_val = ACP_I2STDM_IRER;283 ier_val = ACP_I2STDM_IER;284 buf_reg = ACP_I2S_RX_RINGBUFSIZE;285 }286 }287 acp_writel(period_bytes, rtd->acp5x_base + water_val);288 acp_writel(buf_size, rtd->acp5x_base + buf_reg);289 if (adata->master_mode)290 acp5x_set_i2s_clk(adata, rtd);291 val = acp_readl(rtd->acp5x_base + reg_val);292 val = val | BIT(0);293 acp_writel(val, rtd->acp5x_base + reg_val);294 acp_writel(1, rtd->acp5x_base + ier_val);295 ret = 0;296 break;297 case SNDRV_PCM_TRIGGER_STOP:298 case SNDRV_PCM_TRIGGER_SUSPEND:299 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:300 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {301 switch (rtd->i2s_instance) {302 case I2S_HS_INSTANCE:303 reg_val = ACP_HSTDM_ITER;304 break;305 case I2S_SP_INSTANCE:306 default:307 reg_val = ACP_I2STDM_ITER;308 }309 310 } else {311 switch (rtd->i2s_instance) {312 case I2S_HS_INSTANCE:313 reg_val = ACP_HSTDM_IRER;314 break;315 case I2S_SP_INSTANCE:316 default:317 reg_val = ACP_I2STDM_IRER;318 }319 }320 val = acp_readl(rtd->acp5x_base + reg_val);321 val = val & ~BIT(0);322 acp_writel(val, rtd->acp5x_base + reg_val);323 324 if (!(acp_readl(rtd->acp5x_base + ACP_HSTDM_ITER) & BIT(0)) &&325 !(acp_readl(rtd->acp5x_base + ACP_HSTDM_IRER) & BIT(0)))326 acp_writel(0, rtd->acp5x_base + ACP_HSTDM_IER);327 if (!(acp_readl(rtd->acp5x_base + ACP_I2STDM_ITER) & BIT(0)) &&328 !(acp_readl(rtd->acp5x_base + ACP_I2STDM_IRER) & BIT(0)))329 acp_writel(0, rtd->acp5x_base + ACP_I2STDM_IER);330 ret = 0;331 break;332 default:333 ret = -EINVAL;334 break;335 }336 return ret;337}338 339static const struct snd_soc_dai_ops acp5x_i2s_dai_ops = {340 .hw_params = acp5x_i2s_hwparams,341 .trigger = acp5x_i2s_trigger,342 .set_fmt = acp5x_i2s_set_fmt,343 .set_tdm_slot = acp5x_i2s_set_tdm_slot,344};345 346static const struct snd_soc_component_driver acp5x_dai_component = {347 .name = "acp5x-i2s",348 .legacy_dai_naming = 1,349};350 351static struct snd_soc_dai_driver acp5x_i2s_dai = {352 .playback = {353 .rates = SNDRV_PCM_RATE_8000_96000,354 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |355 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,356 .channels_min = 2,357 .channels_max = 2,358 .rate_min = 8000,359 .rate_max = 96000,360 },361 .capture = {362 .rates = SNDRV_PCM_RATE_8000_96000,363 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |364 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,365 .channels_min = 2,366 .channels_max = 2,367 .rate_min = 8000,368 .rate_max = 96000,369 },370 .ops = &acp5x_i2s_dai_ops,371};372 373static int acp5x_dai_probe(struct platform_device *pdev)374{375 struct resource *res;376 struct i2s_dev_data *adata;377 int ret;378 379 adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data),380 GFP_KERNEL);381 if (!adata)382 return -ENOMEM;383 384 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);385 if (!res) {386 dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");387 return -ENOMEM;388 }389 adata->acp5x_base = devm_ioremap(&pdev->dev, res->start,390 resource_size(res));391 if (!adata->acp5x_base)392 return -ENOMEM;393 394 adata->master_mode = I2S_MASTER_MODE_ENABLE;395 dev_set_drvdata(&pdev->dev, adata);396 ret = devm_snd_soc_register_component(&pdev->dev,397 &acp5x_dai_component,398 &acp5x_i2s_dai, 1);399 if (ret)400 dev_err(&pdev->dev, "Fail to register acp i2s dai\n");401 return ret;402}403 404static struct platform_driver acp5x_dai_driver = {405 .probe = acp5x_dai_probe,406 .driver = {407 .name = "acp5x_i2s_playcap",408 },409};410 411module_platform_driver(acp5x_dai_driver);412 413MODULE_AUTHOR("Vijendar.Mukunda@amd.com");414MODULE_DESCRIPTION("AMD ACP5.x CPU DAI Driver");415MODULE_ALIAS("platform:" DRV_NAME);416MODULE_LICENSE("GPL v2");417