331 lines · c
1// SPDX-License-Identifier: GPL-2.0+2//3// AMD ALSA SoC PCM Driver4//5//Copyright 2016 Advanced Micro Devices, Inc.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 "acp3x.h"17 18#define DRV_NAME "acp3x_i2s_playcap"19 20static int acp3x_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 return 0;39}40 41static int acp3x_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai,42 u32 tx_mask, u32 rx_mask, int slots, int slot_width)43{44 struct i2s_dev_data *adata;45 u32 frm_len;46 u16 slot_len;47 48 adata = snd_soc_dai_get_drvdata(cpu_dai);49 50 /* These values are as per Hardware Spec */51 switch (slot_width) {52 case SLOT_WIDTH_8:53 slot_len = 8;54 break;55 case SLOT_WIDTH_16:56 slot_len = 16;57 break;58 case SLOT_WIDTH_24:59 slot_len = 24;60 break;61 case SLOT_WIDTH_32:62 slot_len = 0;63 break;64 default:65 return -EINVAL;66 }67 frm_len = FRM_LEN | (slots << 15) | (slot_len << 18);68 adata->tdm_fmt = frm_len;69 return 0;70}71 72static int acp3x_i2s_hwparams(struct snd_pcm_substream *substream,73 struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)74{75 struct i2s_stream_instance *rtd;76 struct snd_soc_pcm_runtime *prtd;77 struct snd_soc_card *card;78 struct acp3x_platform_info *pinfo;79 struct i2s_dev_data *adata;80 u32 val;81 u32 reg_val, frmt_reg;82 83 prtd = snd_soc_substream_to_rtd(substream);84 rtd = substream->runtime->private_data;85 card = prtd->card;86 adata = snd_soc_dai_get_drvdata(dai);87 pinfo = snd_soc_card_get_drvdata(card);88 if (pinfo) {89 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)90 rtd->i2s_instance = pinfo->play_i2s_instance;91 else92 rtd->i2s_instance = pinfo->cap_i2s_instance;93 }94 95 /* These values are as per Hardware Spec */96 switch (params_format(params)) {97 case SNDRV_PCM_FORMAT_U8:98 case SNDRV_PCM_FORMAT_S8:99 rtd->xfer_resolution = 0x0;100 break;101 case SNDRV_PCM_FORMAT_S16_LE:102 rtd->xfer_resolution = 0x02;103 break;104 case SNDRV_PCM_FORMAT_S24_LE:105 rtd->xfer_resolution = 0x04;106 break;107 case SNDRV_PCM_FORMAT_S32_LE:108 rtd->xfer_resolution = 0x05;109 break;110 default:111 return -EINVAL;112 }113 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {114 switch (rtd->i2s_instance) {115 case I2S_BT_INSTANCE:116 reg_val = mmACP_BTTDM_ITER;117 frmt_reg = mmACP_BTTDM_TXFRMT;118 break;119 case I2S_SP_INSTANCE:120 default:121 reg_val = mmACP_I2STDM_ITER;122 frmt_reg = mmACP_I2STDM_TXFRMT;123 }124 } else {125 switch (rtd->i2s_instance) {126 case I2S_BT_INSTANCE:127 reg_val = mmACP_BTTDM_IRER;128 frmt_reg = mmACP_BTTDM_RXFRMT;129 break;130 case I2S_SP_INSTANCE:131 default:132 reg_val = mmACP_I2STDM_IRER;133 frmt_reg = mmACP_I2STDM_RXFRMT;134 }135 }136 if (adata->tdm_mode) {137 val = rv_readl(rtd->acp3x_base + reg_val);138 rv_writel(val | 0x2, rtd->acp3x_base + reg_val);139 rv_writel(adata->tdm_fmt, rtd->acp3x_base + frmt_reg);140 }141 val = rv_readl(rtd->acp3x_base + reg_val);142 val &= ~ACP3x_ITER_IRER_SAMP_LEN_MASK;143 val = val | (rtd->xfer_resolution << 3);144 rv_writel(val, rtd->acp3x_base + reg_val);145 return 0;146}147 148static int acp3x_i2s_trigger(struct snd_pcm_substream *substream,149 int cmd, struct snd_soc_dai *dai)150{151 struct i2s_stream_instance *rtd;152 u32 ret, val, period_bytes, reg_val, ier_val, water_val;153 u32 buf_size, buf_reg;154 155 rtd = substream->runtime->private_data;156 period_bytes = frames_to_bytes(substream->runtime,157 substream->runtime->period_size);158 buf_size = frames_to_bytes(substream->runtime,159 substream->runtime->buffer_size);160 switch (cmd) {161 case SNDRV_PCM_TRIGGER_START:162 case SNDRV_PCM_TRIGGER_RESUME:163 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:164 rtd->bytescount = acp_get_byte_count(rtd,165 substream->stream);166 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {167 switch (rtd->i2s_instance) {168 case I2S_BT_INSTANCE:169 water_val =170 mmACP_BT_TX_INTR_WATERMARK_SIZE;171 reg_val = mmACP_BTTDM_ITER;172 ier_val = mmACP_BTTDM_IER;173 buf_reg = mmACP_BT_TX_RINGBUFSIZE;174 break;175 case I2S_SP_INSTANCE:176 default:177 water_val =178 mmACP_I2S_TX_INTR_WATERMARK_SIZE;179 reg_val = mmACP_I2STDM_ITER;180 ier_val = mmACP_I2STDM_IER;181 buf_reg = mmACP_I2S_TX_RINGBUFSIZE;182 }183 } else {184 switch (rtd->i2s_instance) {185 case I2S_BT_INSTANCE:186 water_val =187 mmACP_BT_RX_INTR_WATERMARK_SIZE;188 reg_val = mmACP_BTTDM_IRER;189 ier_val = mmACP_BTTDM_IER;190 buf_reg = mmACP_BT_RX_RINGBUFSIZE;191 break;192 case I2S_SP_INSTANCE:193 default:194 water_val =195 mmACP_I2S_RX_INTR_WATERMARK_SIZE;196 reg_val = mmACP_I2STDM_IRER;197 ier_val = mmACP_I2STDM_IER;198 buf_reg = mmACP_I2S_RX_RINGBUFSIZE;199 }200 }201 rv_writel(period_bytes, rtd->acp3x_base + water_val);202 rv_writel(buf_size, rtd->acp3x_base + buf_reg);203 val = rv_readl(rtd->acp3x_base + reg_val);204 val = val | BIT(0);205 rv_writel(val, rtd->acp3x_base + reg_val);206 rv_writel(1, rtd->acp3x_base + ier_val);207 ret = 0;208 break;209 case SNDRV_PCM_TRIGGER_STOP:210 case SNDRV_PCM_TRIGGER_SUSPEND:211 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:212 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {213 switch (rtd->i2s_instance) {214 case I2S_BT_INSTANCE:215 reg_val = mmACP_BTTDM_ITER;216 break;217 case I2S_SP_INSTANCE:218 default:219 reg_val = mmACP_I2STDM_ITER;220 }221 222 } else {223 switch (rtd->i2s_instance) {224 case I2S_BT_INSTANCE:225 reg_val = mmACP_BTTDM_IRER;226 break;227 case I2S_SP_INSTANCE:228 default:229 reg_val = mmACP_I2STDM_IRER;230 }231 }232 val = rv_readl(rtd->acp3x_base + reg_val);233 val = val & ~BIT(0);234 rv_writel(val, rtd->acp3x_base + reg_val);235 236 if (!(rv_readl(rtd->acp3x_base + mmACP_BTTDM_ITER) & BIT(0)) &&237 !(rv_readl(rtd->acp3x_base + mmACP_BTTDM_IRER) & BIT(0)))238 rv_writel(0, rtd->acp3x_base + mmACP_BTTDM_IER);239 if (!(rv_readl(rtd->acp3x_base + mmACP_I2STDM_ITER) & BIT(0)) &&240 !(rv_readl(rtd->acp3x_base + mmACP_I2STDM_IRER) & BIT(0)))241 rv_writel(0, rtd->acp3x_base + mmACP_I2STDM_IER);242 ret = 0;243 break;244 default:245 ret = -EINVAL;246 break;247 }248 249 return ret;250}251 252static const struct snd_soc_dai_ops acp3x_i2s_dai_ops = {253 .hw_params = acp3x_i2s_hwparams,254 .trigger = acp3x_i2s_trigger,255 .set_fmt = acp3x_i2s_set_fmt,256 .set_tdm_slot = acp3x_i2s_set_tdm_slot,257};258 259static const struct snd_soc_component_driver acp3x_dai_component = {260 .name = DRV_NAME,261 .legacy_dai_naming = 1,262};263 264static struct snd_soc_dai_driver acp3x_i2s_dai = {265 .playback = {266 .rates = SNDRV_PCM_RATE_8000_96000,267 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |268 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,269 .channels_min = 2,270 .channels_max = 8,271 .rate_min = 8000,272 .rate_max = 96000,273 },274 .capture = {275 .rates = SNDRV_PCM_RATE_8000_48000,276 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |277 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,278 .channels_min = 2,279 .channels_max = 2,280 .rate_min = 8000,281 .rate_max = 48000,282 },283 .ops = &acp3x_i2s_dai_ops,284};285 286static int acp3x_dai_probe(struct platform_device *pdev)287{288 struct resource *res;289 struct i2s_dev_data *adata;290 int ret;291 292 adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data),293 GFP_KERNEL);294 if (!adata)295 return -ENOMEM;296 297 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);298 if (!res) {299 dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");300 return -ENOMEM;301 }302 adata->acp3x_base = devm_ioremap(&pdev->dev, res->start,303 resource_size(res));304 if (!adata->acp3x_base)305 return -ENOMEM;306 307 adata->i2s_irq = res->start;308 dev_set_drvdata(&pdev->dev, adata);309 ret = devm_snd_soc_register_component(&pdev->dev,310 &acp3x_dai_component, &acp3x_i2s_dai, 1);311 if (ret) {312 dev_err(&pdev->dev, "Fail to register acp i2s dai\n");313 return -ENODEV;314 }315 return 0;316}317 318static struct platform_driver acp3x_dai_driver = {319 .probe = acp3x_dai_probe,320 .driver = {321 .name = "acp3x_i2s_playcap",322 },323};324 325module_platform_driver(acp3x_dai_driver);326 327MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");328MODULE_DESCRIPTION("AMD ACP 3.x PCM Driver");329MODULE_LICENSE("GPL v2");330MODULE_ALIAS("platform:"DRV_NAME);331