linux/sound/soc/amd/raven/acp3x-i2s.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2//
   3// AMD ALSA SoC PCM Driver
   4//
   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 = asoc_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                else
  92                        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};
 262
 263static struct snd_soc_dai_driver acp3x_i2s_dai = {
 264        .playback = {
 265                .rates = SNDRV_PCM_RATE_8000_96000,
 266                .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
 267                        SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
 268                .channels_min = 2,
 269                .channels_max = 8,
 270                .rate_min = 8000,
 271                .rate_max = 96000,
 272        },
 273        .capture = {
 274                .rates = SNDRV_PCM_RATE_8000_48000,
 275                .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
 276                        SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
 277                .channels_min = 2,
 278                .channels_max = 2,
 279                .rate_min = 8000,
 280                .rate_max = 48000,
 281        },
 282        .ops = &acp3x_i2s_dai_ops,
 283};
 284
 285static int acp3x_dai_probe(struct platform_device *pdev)
 286{
 287        struct resource *res;
 288        struct i2s_dev_data *adata;
 289        int ret;
 290
 291        adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data),
 292                        GFP_KERNEL);
 293        if (!adata)
 294                return -ENOMEM;
 295
 296        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 297        if (!res) {
 298                dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
 299                return -ENOMEM;
 300        }
 301        adata->acp3x_base = devm_ioremap(&pdev->dev, res->start,
 302                                                resource_size(res));
 303        if (!adata->acp3x_base)
 304                return -ENOMEM;
 305
 306        adata->i2s_irq = res->start;
 307        dev_set_drvdata(&pdev->dev, adata);
 308        ret = devm_snd_soc_register_component(&pdev->dev,
 309                        &acp3x_dai_component, &acp3x_i2s_dai, 1);
 310        if (ret) {
 311                dev_err(&pdev->dev, "Fail to register acp i2s dai\n");
 312                return -ENODEV;
 313        }
 314        return 0;
 315}
 316
 317static int acp3x_dai_remove(struct platform_device *pdev)
 318{
 319        /* As we use devm_ memory alloc there is nothing TBD here */
 320
 321        return 0;
 322}
 323
 324static struct platform_driver acp3x_dai_driver = {
 325        .probe = acp3x_dai_probe,
 326        .remove = acp3x_dai_remove,
 327        .driver = {
 328                .name = "acp3x_i2s_playcap",
 329        },
 330};
 331
 332module_platform_driver(acp3x_dai_driver);
 333
 334MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");
 335MODULE_DESCRIPTION("AMD ACP 3.x PCM Driver");
 336MODULE_LICENSE("GPL v2");
 337MODULE_ALIAS("platform:"DRV_NAME);
 338