Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 //
0003 // AMD ALSA SoC PCM Driver
0004 //
0005 // Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
0006 
0007 #include <linux/platform_device.h>
0008 #include <linux/module.h>
0009 #include <linux/err.h>
0010 #include <linux/io.h>
0011 #include <sound/pcm_params.h>
0012 #include <sound/soc.h>
0013 #include <sound/soc-dai.h>
0014 #include <linux/dma-mapping.h>
0015 
0016 #include "acp5x.h"
0017 
0018 #define DRV_NAME "acp5x_i2s_playcap"
0019 
0020 static int acp5x_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
0021                  unsigned int fmt)
0022 {
0023     struct i2s_dev_data *adata;
0024     int mode;
0025 
0026     adata = snd_soc_dai_get_drvdata(cpu_dai);
0027     mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
0028     switch (mode) {
0029     case SND_SOC_DAIFMT_I2S:
0030         adata->tdm_mode = TDM_DISABLE;
0031         break;
0032     case SND_SOC_DAIFMT_DSP_A:
0033         adata->tdm_mode = TDM_ENABLE;
0034         break;
0035     default:
0036         return -EINVAL;
0037     }
0038     mode = fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
0039     switch (mode) {
0040     case SND_SOC_DAIFMT_BP_FP:
0041         adata->master_mode = I2S_MASTER_MODE_ENABLE;
0042         break;
0043     case SND_SOC_DAIFMT_BC_FC:
0044         adata->master_mode = I2S_MASTER_MODE_DISABLE;
0045         break;
0046     }
0047     return 0;
0048 }
0049 
0050 static int acp5x_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai,
0051                   u32 tx_mask, u32 rx_mask,
0052                   int slots, int slot_width)
0053 {
0054     struct i2s_dev_data *adata;
0055     u32 frm_len;
0056     u16 slot_len;
0057 
0058     adata = snd_soc_dai_get_drvdata(cpu_dai);
0059 
0060     /* These values are as per Hardware Spec */
0061     switch (slot_width) {
0062     case SLOT_WIDTH_8:
0063         slot_len = 8;
0064         break;
0065     case SLOT_WIDTH_16:
0066         slot_len = 16;
0067         break;
0068     case SLOT_WIDTH_24:
0069         slot_len = 24;
0070         break;
0071     case SLOT_WIDTH_32:
0072         slot_len = 0;
0073         break;
0074     default:
0075         return -EINVAL;
0076     }
0077     frm_len = FRM_LEN | (slots << 15) | (slot_len << 18);
0078     adata->tdm_fmt = frm_len;
0079     return 0;
0080 }
0081 
0082 static int acp5x_i2s_hwparams(struct snd_pcm_substream *substream,
0083                   struct snd_pcm_hw_params *params,
0084                   struct snd_soc_dai *dai)
0085 {
0086     struct i2s_stream_instance *rtd;
0087     struct snd_soc_pcm_runtime *prtd;
0088     struct snd_soc_card *card;
0089     struct acp5x_platform_info *pinfo;
0090     struct i2s_dev_data *adata;
0091 
0092     u32 val;
0093     u32 reg_val, frmt_reg;
0094     u32 lrclk_div_val, bclk_div_val;
0095 
0096     lrclk_div_val = 0;
0097     bclk_div_val = 0;
0098     prtd = asoc_substream_to_rtd(substream);
0099     rtd = substream->runtime->private_data;
0100     card = prtd->card;
0101     adata = snd_soc_dai_get_drvdata(dai);
0102     pinfo = snd_soc_card_get_drvdata(card);
0103     if (pinfo) {
0104         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
0105             rtd->i2s_instance = pinfo->play_i2s_instance;
0106         else
0107             rtd->i2s_instance = pinfo->cap_i2s_instance;
0108     }
0109 
0110     /* These values are as per Hardware Spec */
0111     switch (params_format(params)) {
0112     case SNDRV_PCM_FORMAT_U8:
0113     case SNDRV_PCM_FORMAT_S8:
0114         rtd->xfer_resolution = 0x0;
0115         break;
0116     case SNDRV_PCM_FORMAT_S16_LE:
0117         rtd->xfer_resolution = 0x02;
0118         break;
0119     case SNDRV_PCM_FORMAT_S24_LE:
0120         rtd->xfer_resolution = 0x04;
0121         break;
0122     case SNDRV_PCM_FORMAT_S32_LE:
0123         rtd->xfer_resolution = 0x05;
0124         break;
0125     default:
0126         return -EINVAL;
0127     }
0128     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
0129         switch (rtd->i2s_instance) {
0130         case I2S_HS_INSTANCE:
0131             reg_val = ACP_HSTDM_ITER;
0132             frmt_reg = ACP_HSTDM_TXFRMT;
0133             break;
0134         case I2S_SP_INSTANCE:
0135         default:
0136             reg_val = ACP_I2STDM_ITER;
0137             frmt_reg = ACP_I2STDM_TXFRMT;
0138         }
0139     } else {
0140         switch (rtd->i2s_instance) {
0141         case I2S_HS_INSTANCE:
0142             reg_val = ACP_HSTDM_IRER;
0143             frmt_reg = ACP_HSTDM_RXFRMT;
0144             break;
0145         case I2S_SP_INSTANCE:
0146         default:
0147             reg_val = ACP_I2STDM_IRER;
0148             frmt_reg = ACP_I2STDM_RXFRMT;
0149         }
0150     }
0151     if (adata->tdm_mode) {
0152         val = acp_readl(rtd->acp5x_base + reg_val);
0153         acp_writel(val | 0x2, rtd->acp5x_base + reg_val);
0154         acp_writel(adata->tdm_fmt, rtd->acp5x_base + frmt_reg);
0155     }
0156     val = acp_readl(rtd->acp5x_base + reg_val);
0157     val &= ~ACP5x_ITER_IRER_SAMP_LEN_MASK;
0158     val = val | (rtd->xfer_resolution  << 3);
0159     acp_writel(val, rtd->acp5x_base + reg_val);
0160 
0161     if (adata->master_mode) {
0162         switch (params_format(params)) {
0163         case SNDRV_PCM_FORMAT_S16_LE:
0164             switch (params_rate(params)) {
0165             case 8000:
0166                 bclk_div_val = 768;
0167                 break;
0168             case 16000:
0169                 bclk_div_val = 384;
0170                 break;
0171             case 24000:
0172                 bclk_div_val = 256;
0173                 break;
0174             case 32000:
0175                 bclk_div_val = 192;
0176                 break;
0177             case 44100:
0178             case 48000:
0179                 bclk_div_val = 128;
0180                 break;
0181             case 88200:
0182             case 96000:
0183                 bclk_div_val = 64;
0184                 break;
0185             case 192000:
0186                 bclk_div_val = 32;
0187                 break;
0188             default:
0189                 return -EINVAL;
0190             }
0191             lrclk_div_val = 32;
0192             break;
0193         case SNDRV_PCM_FORMAT_S32_LE:
0194             switch (params_rate(params)) {
0195             case 8000:
0196                 bclk_div_val = 384;
0197                 break;
0198             case 16000:
0199                 bclk_div_val = 192;
0200                 break;
0201             case 24000:
0202                 bclk_div_val = 128;
0203                 break;
0204             case 32000:
0205                 bclk_div_val = 96;
0206                 break;
0207             case 44100:
0208             case 48000:
0209                 bclk_div_val = 64;
0210                 break;
0211             case 88200:
0212             case 96000:
0213                 bclk_div_val = 32;
0214                 break;
0215             case 192000:
0216                 bclk_div_val = 16;
0217                 break;
0218             default:
0219                 return -EINVAL;
0220             }
0221             lrclk_div_val = 64;
0222             break;
0223         default:
0224             return -EINVAL;
0225         }
0226         rtd->lrclk_div = lrclk_div_val;
0227         rtd->bclk_div = bclk_div_val;
0228     }
0229     return 0;
0230 }
0231 
0232 static int acp5x_i2s_trigger(struct snd_pcm_substream *substream,
0233                  int cmd, struct snd_soc_dai *dai)
0234 {
0235     struct i2s_stream_instance *rtd;
0236     struct i2s_dev_data *adata;
0237     u32 ret, val, period_bytes, reg_val, ier_val, water_val;
0238     u32 buf_size, buf_reg;
0239 
0240     adata = snd_soc_dai_get_drvdata(dai);
0241     rtd = substream->runtime->private_data;
0242     period_bytes = frames_to_bytes(substream->runtime,
0243                        substream->runtime->period_size);
0244     buf_size = frames_to_bytes(substream->runtime,
0245                    substream->runtime->buffer_size);
0246     switch (cmd) {
0247     case SNDRV_PCM_TRIGGER_START:
0248     case SNDRV_PCM_TRIGGER_RESUME:
0249     case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
0250         rtd->bytescount = acp_get_byte_count(rtd,
0251                              substream->stream);
0252         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
0253             switch (rtd->i2s_instance) {
0254             case I2S_HS_INSTANCE:
0255                 water_val =
0256                     ACP_HS_TX_INTR_WATERMARK_SIZE;
0257                 reg_val = ACP_HSTDM_ITER;
0258                 ier_val = ACP_HSTDM_IER;
0259                 buf_reg = ACP_HS_TX_RINGBUFSIZE;
0260                 break;
0261             case I2S_SP_INSTANCE:
0262             default:
0263                 water_val =
0264                     ACP_I2S_TX_INTR_WATERMARK_SIZE;
0265                 reg_val = ACP_I2STDM_ITER;
0266                 ier_val = ACP_I2STDM_IER;
0267                 buf_reg = ACP_I2S_TX_RINGBUFSIZE;
0268             }
0269         } else {
0270             switch (rtd->i2s_instance) {
0271             case I2S_HS_INSTANCE:
0272                 water_val =
0273                     ACP_HS_RX_INTR_WATERMARK_SIZE;
0274                 reg_val = ACP_HSTDM_IRER;
0275                 ier_val = ACP_HSTDM_IER;
0276                 buf_reg = ACP_HS_RX_RINGBUFSIZE;
0277                 break;
0278             case I2S_SP_INSTANCE:
0279             default:
0280                 water_val =
0281                     ACP_I2S_RX_INTR_WATERMARK_SIZE;
0282                 reg_val = ACP_I2STDM_IRER;
0283                 ier_val = ACP_I2STDM_IER;
0284                 buf_reg = ACP_I2S_RX_RINGBUFSIZE;
0285             }
0286         }
0287         acp_writel(period_bytes, rtd->acp5x_base + water_val);
0288         acp_writel(buf_size, rtd->acp5x_base + buf_reg);
0289         if (adata->master_mode)
0290             acp5x_set_i2s_clk(adata, rtd);
0291         val = acp_readl(rtd->acp5x_base + reg_val);
0292         val = val | BIT(0);
0293         acp_writel(val, rtd->acp5x_base + reg_val);
0294         acp_writel(1, rtd->acp5x_base + ier_val);
0295         ret = 0;
0296         break;
0297     case SNDRV_PCM_TRIGGER_STOP:
0298     case SNDRV_PCM_TRIGGER_SUSPEND:
0299     case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
0300         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
0301             switch (rtd->i2s_instance) {
0302             case I2S_HS_INSTANCE:
0303                 reg_val = ACP_HSTDM_ITER;
0304                 break;
0305             case I2S_SP_INSTANCE:
0306             default:
0307                 reg_val = ACP_I2STDM_ITER;
0308             }
0309 
0310         } else {
0311             switch (rtd->i2s_instance) {
0312             case I2S_HS_INSTANCE:
0313                 reg_val = ACP_HSTDM_IRER;
0314                 break;
0315             case I2S_SP_INSTANCE:
0316             default:
0317                 reg_val = ACP_I2STDM_IRER;
0318             }
0319         }
0320         val = acp_readl(rtd->acp5x_base + reg_val);
0321         val = val & ~BIT(0);
0322         acp_writel(val, rtd->acp5x_base + reg_val);
0323 
0324         if (!(acp_readl(rtd->acp5x_base + ACP_HSTDM_ITER) & BIT(0)) &&
0325             !(acp_readl(rtd->acp5x_base + ACP_HSTDM_IRER) & BIT(0)))
0326             acp_writel(0, rtd->acp5x_base + ACP_HSTDM_IER);
0327         if (!(acp_readl(rtd->acp5x_base + ACP_I2STDM_ITER) & BIT(0)) &&
0328             !(acp_readl(rtd->acp5x_base + ACP_I2STDM_IRER) & BIT(0)))
0329             acp_writel(0, rtd->acp5x_base + ACP_I2STDM_IER);
0330         ret = 0;
0331         break;
0332     default:
0333         ret = -EINVAL;
0334         break;
0335     }
0336     return ret;
0337 }
0338 
0339 static const struct snd_soc_dai_ops acp5x_i2s_dai_ops = {
0340     .hw_params = acp5x_i2s_hwparams,
0341     .trigger = acp5x_i2s_trigger,
0342     .set_fmt = acp5x_i2s_set_fmt,
0343     .set_tdm_slot = acp5x_i2s_set_tdm_slot,
0344 };
0345 
0346 static const struct snd_soc_component_driver acp5x_dai_component = {
0347     .name = "acp5x-i2s",
0348     .legacy_dai_naming = 1,
0349 };
0350 
0351 static struct snd_soc_dai_driver acp5x_i2s_dai = {
0352     .playback = {
0353         .rates = SNDRV_PCM_RATE_8000_96000,
0354         .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
0355             SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
0356         .channels_min = 2,
0357         .channels_max = 2,
0358         .rate_min = 8000,
0359         .rate_max = 96000,
0360     },
0361     .capture = {
0362         .rates = SNDRV_PCM_RATE_8000_96000,
0363         .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
0364             SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
0365         .channels_min = 2,
0366         .channels_max = 2,
0367         .rate_min = 8000,
0368         .rate_max = 96000,
0369     },
0370     .ops = &acp5x_i2s_dai_ops,
0371 };
0372 
0373 static int acp5x_dai_probe(struct platform_device *pdev)
0374 {
0375     struct resource *res;
0376     struct i2s_dev_data *adata;
0377     int ret;
0378 
0379     adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data),
0380                  GFP_KERNEL);
0381     if (!adata)
0382         return -ENOMEM;
0383 
0384     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0385     if (!res) {
0386         dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
0387         return -ENOMEM;
0388     }
0389     adata->acp5x_base = devm_ioremap(&pdev->dev, res->start,
0390                      resource_size(res));
0391     if (!adata->acp5x_base)
0392         return -ENOMEM;
0393 
0394     adata->master_mode = I2S_MASTER_MODE_ENABLE;
0395     dev_set_drvdata(&pdev->dev, adata);
0396     ret = devm_snd_soc_register_component(&pdev->dev,
0397                           &acp5x_dai_component,
0398                           &acp5x_i2s_dai, 1);
0399     if (ret)
0400         dev_err(&pdev->dev, "Fail to register acp i2s dai\n");
0401     return ret;
0402 }
0403 
0404 static struct platform_driver acp5x_dai_driver = {
0405     .probe = acp5x_dai_probe,
0406     .driver = {
0407         .name = "acp5x_i2s_playcap",
0408     },
0409 };
0410 
0411 module_platform_driver(acp5x_dai_driver);
0412 
0413 MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
0414 MODULE_DESCRIPTION("AMD ACP5.x CPU DAI Driver");
0415 MODULE_ALIAS("platform:" DRV_NAME);
0416 MODULE_LICENSE("GPL v2");