linux/sound/soc/amd/renoir/acp3x-pdm-dma.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2//
   3// AMD ALSA SoC PDM Driver
   4//
   5//Copyright 2020 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 <linux/pm_runtime.h>
  12#include <sound/pcm_params.h>
  13#include <sound/soc.h>
  14#include <sound/soc-dai.h>
  15
  16#include "rn_acp3x.h"
  17
  18#define DRV_NAME "acp_rn_pdm_dma"
  19
  20static const struct snd_pcm_hardware acp_pdm_hardware_capture = {
  21        .info = SNDRV_PCM_INFO_INTERLEAVED |
  22                SNDRV_PCM_INFO_BLOCK_TRANSFER |
  23                SNDRV_PCM_INFO_MMAP |
  24                SNDRV_PCM_INFO_MMAP_VALID |
  25                SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
  26        .formats = SNDRV_PCM_FMTBIT_S32_LE,
  27        .channels_min = 2,
  28        .channels_max = 2,
  29        .rates = SNDRV_PCM_RATE_48000,
  30        .rate_min = 48000,
  31        .rate_max = 48000,
  32        .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
  33        .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
  34        .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
  35        .periods_min = CAPTURE_MIN_NUM_PERIODS,
  36        .periods_max = CAPTURE_MAX_NUM_PERIODS,
  37};
  38
  39static irqreturn_t pdm_irq_handler(int irq, void *dev_id)
  40{
  41        struct pdm_dev_data *rn_pdm_data;
  42        u16 cap_flag;
  43        u32 val;
  44
  45        rn_pdm_data = dev_id;
  46        if (!rn_pdm_data)
  47                return IRQ_NONE;
  48
  49        cap_flag = 0;
  50        val = rn_readl(rn_pdm_data->acp_base + ACP_EXTERNAL_INTR_STAT);
  51        if ((val & BIT(PDM_DMA_STAT)) && rn_pdm_data->capture_stream) {
  52                rn_writel(BIT(PDM_DMA_STAT), rn_pdm_data->acp_base +
  53                          ACP_EXTERNAL_INTR_STAT);
  54                snd_pcm_period_elapsed(rn_pdm_data->capture_stream);
  55                cap_flag = 1;
  56        }
  57
  58        if (cap_flag)
  59                return IRQ_HANDLED;
  60        else
  61                return IRQ_NONE;
  62}
  63
  64static void init_pdm_ring_buffer(u32 physical_addr,
  65                                 u32 buffer_size,
  66                                 u32 watermark_size,
  67                                 void __iomem *acp_base)
  68{
  69        rn_writel(physical_addr, acp_base + ACP_WOV_RX_RINGBUFADDR);
  70        rn_writel(buffer_size, acp_base + ACP_WOV_RX_RINGBUFSIZE);
  71        rn_writel(watermark_size, acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
  72        rn_writel(0x01, acp_base + ACPAXI2AXI_ATU_CTRL);
  73}
  74
  75static void enable_pdm_clock(void __iomem *acp_base)
  76{
  77        u32 pdm_clk_enable, pdm_ctrl;
  78
  79        pdm_clk_enable = ACP_PDM_CLK_FREQ_MASK;
  80
  81        rn_writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL);
  82        pdm_ctrl = rn_readl(acp_base + ACP_WOV_MISC_CTRL);
  83        pdm_ctrl |= ACP_WOV_MISC_CTRL_MASK;
  84        rn_writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL);
  85}
  86
  87static void enable_pdm_interrupts(void __iomem *acp_base)
  88{
  89        u32 ext_int_ctrl;
  90
  91        ext_int_ctrl = rn_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
  92        ext_int_ctrl |= PDM_DMA_INTR_MASK;
  93        rn_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
  94}
  95
  96static void disable_pdm_interrupts(void __iomem *acp_base)
  97{
  98        u32 ext_int_ctrl;
  99
 100        ext_int_ctrl = rn_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
 101        ext_int_ctrl |= ~PDM_DMA_INTR_MASK;
 102        rn_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
 103}
 104
 105static bool check_pdm_dma_status(void __iomem *acp_base)
 106{
 107        bool pdm_dma_status;
 108        u32 pdm_enable, pdm_dma_enable;
 109
 110        pdm_dma_status = false;
 111        pdm_enable = rn_readl(acp_base + ACP_WOV_PDM_ENABLE);
 112        pdm_dma_enable = rn_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
 113        if ((pdm_enable & ACP_PDM_ENABLE) && (pdm_dma_enable &
 114             ACP_PDM_DMA_EN_STATUS))
 115                pdm_dma_status = true;
 116        return pdm_dma_status;
 117}
 118
 119static int start_pdm_dma(void __iomem *acp_base)
 120{
 121        u32 pdm_enable;
 122        u32 pdm_dma_enable;
 123        int timeout;
 124
 125        pdm_enable = 0x01;
 126        pdm_dma_enable  = 0x01;
 127
 128        enable_pdm_clock(acp_base);
 129        rn_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
 130        rn_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
 131        timeout = 0;
 132        while (++timeout < ACP_COUNTER) {
 133                pdm_dma_enable = rn_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
 134                if ((pdm_dma_enable & 0x02) == ACP_PDM_DMA_EN_STATUS)
 135                        return 0;
 136                udelay(DELAY_US);
 137        }
 138        return -ETIMEDOUT;
 139}
 140
 141static int stop_pdm_dma(void __iomem *acp_base)
 142{
 143        u32 pdm_enable, pdm_dma_enable;
 144        int timeout;
 145
 146        pdm_enable = rn_readl(acp_base + ACP_WOV_PDM_ENABLE);
 147        pdm_dma_enable = rn_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
 148        if (pdm_dma_enable & 0x01) {
 149                pdm_dma_enable = 0x02;
 150                rn_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
 151                timeout = 0;
 152                while (++timeout < ACP_COUNTER) {
 153                        pdm_dma_enable = rn_readl(acp_base +
 154                                                  ACP_WOV_PDM_DMA_ENABLE);
 155                        if ((pdm_dma_enable & 0x02) == 0x00)
 156                                break;
 157                        udelay(DELAY_US);
 158                }
 159                if (timeout == ACP_COUNTER)
 160                        return -ETIMEDOUT;
 161        }
 162        if (pdm_enable == ACP_PDM_ENABLE) {
 163                pdm_enable = ACP_PDM_DISABLE;
 164                rn_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
 165        }
 166        rn_writel(0x01, acp_base + ACP_WOV_PDM_FIFO_FLUSH);
 167        return 0;
 168}
 169
 170static void config_acp_dma(struct pdm_stream_instance *rtd, int direction)
 171{
 172        u16 page_idx;
 173        u32 low, high, val;
 174        dma_addr_t addr;
 175
 176        addr = rtd->dma_addr;
 177        val = 0;
 178
 179        /* Group Enable */
 180        rn_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp_base +
 181                  ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
 182        rn_writel(PAGE_SIZE_4K_ENABLE, rtd->acp_base +
 183                  ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
 184
 185        for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) {
 186                /* Load the low address of page int ACP SRAM through SRBM */
 187                low = lower_32_bits(addr);
 188                high = upper_32_bits(addr);
 189
 190                rn_writel(low, rtd->acp_base + ACP_SCRATCH_REG_0 + val);
 191                high |= BIT(31);
 192                rn_writel(high, rtd->acp_base + ACP_SCRATCH_REG_0 + val + 4);
 193                val += 8;
 194                addr += PAGE_SIZE;
 195        }
 196}
 197
 198static int acp_pdm_dma_open(struct snd_soc_component *component,
 199                            struct snd_pcm_substream *substream)
 200{
 201        struct snd_pcm_runtime *runtime;
 202        struct pdm_dev_data *adata;
 203        struct pdm_stream_instance *pdm_data;
 204        int ret;
 205
 206        runtime = substream->runtime;
 207        adata = dev_get_drvdata(component->dev);
 208        pdm_data = kzalloc(sizeof(*pdm_data), GFP_KERNEL);
 209        if (!pdm_data)
 210                return -EINVAL;
 211
 212        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
 213                runtime->hw = acp_pdm_hardware_capture;
 214
 215        ret = snd_pcm_hw_constraint_integer(runtime,
 216                                            SNDRV_PCM_HW_PARAM_PERIODS);
 217        if (ret < 0) {
 218                dev_err(component->dev, "set integer constraint failed\n");
 219                kfree(pdm_data);
 220                return ret;
 221        }
 222
 223        enable_pdm_interrupts(adata->acp_base);
 224
 225        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
 226                adata->capture_stream = substream;
 227
 228        pdm_data->acp_base = adata->acp_base;
 229        runtime->private_data = pdm_data;
 230        return ret;
 231}
 232
 233static int acp_pdm_dma_hw_params(struct snd_soc_component *component,
 234                                 struct snd_pcm_substream *substream,
 235                                 struct snd_pcm_hw_params *params)
 236{
 237        struct pdm_stream_instance *rtd;
 238        size_t size, period_bytes;
 239
 240        rtd = substream->runtime->private_data;
 241        if (!rtd)
 242                return -EINVAL;
 243        size = params_buffer_bytes(params);
 244        period_bytes = params_period_bytes(params);
 245        rtd->dma_addr = substream->runtime->dma_addr;
 246        rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
 247        config_acp_dma(rtd, substream->stream);
 248        init_pdm_ring_buffer(MEM_WINDOW_START, size, period_bytes,
 249                             rtd->acp_base);
 250        return 0;
 251}
 252
 253static u64 acp_pdm_get_byte_count(struct pdm_stream_instance *rtd,
 254                                  int direction)
 255{
 256        union acp_pdm_dma_count byte_count;
 257
 258        byte_count.bcount.high =
 259                        rn_readl(rtd->acp_base +
 260                                 ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
 261        byte_count.bcount.low =
 262                        rn_readl(rtd->acp_base +
 263                                 ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
 264        return byte_count.bytescount;
 265}
 266
 267static snd_pcm_uframes_t acp_pdm_dma_pointer(struct snd_soc_component *comp,
 268                                             struct snd_pcm_substream *stream)
 269{
 270        struct pdm_stream_instance *rtd;
 271        u32 pos, buffersize;
 272        u64 bytescount;
 273
 274        rtd = stream->runtime->private_data;
 275        buffersize = frames_to_bytes(stream->runtime,
 276                                     stream->runtime->buffer_size);
 277        bytescount = acp_pdm_get_byte_count(rtd, stream->stream);
 278        if (bytescount > rtd->bytescount)
 279                bytescount -= rtd->bytescount;
 280        pos = do_div(bytescount, buffersize);
 281        return bytes_to_frames(stream->runtime, pos);
 282}
 283
 284static int acp_pdm_dma_new(struct snd_soc_component *component,
 285                           struct snd_soc_pcm_runtime *rtd)
 286{
 287        struct device *parent = component->dev->parent;
 288
 289        snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
 290                                       parent, MIN_BUFFER, MAX_BUFFER);
 291        return 0;
 292}
 293
 294static int acp_pdm_dma_close(struct snd_soc_component *component,
 295                             struct snd_pcm_substream *substream)
 296{
 297        struct pdm_dev_data *adata = dev_get_drvdata(component->dev);
 298
 299        disable_pdm_interrupts(adata->acp_base);
 300        adata->capture_stream = NULL;
 301        return 0;
 302}
 303
 304static int acp_pdm_dai_trigger(struct snd_pcm_substream *substream,
 305                               int cmd, struct snd_soc_dai *dai)
 306{
 307        struct pdm_stream_instance *rtd;
 308        int ret;
 309        bool pdm_status;
 310        unsigned int ch_mask;
 311
 312        rtd = substream->runtime->private_data;
 313        ret = 0;
 314        switch (substream->runtime->channels) {
 315        case TWO_CH:
 316                ch_mask = 0x00;
 317                break;
 318        default:
 319                return -EINVAL;
 320        }
 321        switch (cmd) {
 322        case SNDRV_PCM_TRIGGER_START:
 323        case SNDRV_PCM_TRIGGER_RESUME:
 324        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 325                rn_writel(ch_mask, rtd->acp_base + ACP_WOV_PDM_NO_OF_CHANNELS);
 326                rn_writel(PDM_DECIMATION_FACTOR, rtd->acp_base +
 327                          ACP_WOV_PDM_DECIMATION_FACTOR);
 328                rtd->bytescount = acp_pdm_get_byte_count(rtd,
 329                                                         substream->stream);
 330                pdm_status = check_pdm_dma_status(rtd->acp_base);
 331                if (!pdm_status)
 332                        ret = start_pdm_dma(rtd->acp_base);
 333                break;
 334        case SNDRV_PCM_TRIGGER_STOP:
 335        case SNDRV_PCM_TRIGGER_SUSPEND:
 336        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 337                pdm_status = check_pdm_dma_status(rtd->acp_base);
 338                if (pdm_status)
 339                        ret = stop_pdm_dma(rtd->acp_base);
 340                break;
 341        default:
 342                ret = -EINVAL;
 343                break;
 344        }
 345        return ret;
 346}
 347
 348static const struct snd_soc_dai_ops acp_pdm_dai_ops = {
 349        .trigger   = acp_pdm_dai_trigger,
 350};
 351
 352static struct snd_soc_dai_driver acp_pdm_dai_driver = {
 353        .capture = {
 354                .rates = SNDRV_PCM_RATE_48000,
 355                .formats = SNDRV_PCM_FMTBIT_S24_LE |
 356                           SNDRV_PCM_FMTBIT_S32_LE,
 357                .channels_min = 2,
 358                .channels_max = 2,
 359                .rate_min = 48000,
 360                .rate_max = 48000,
 361        },
 362        .ops = &acp_pdm_dai_ops,
 363};
 364
 365static const struct snd_soc_component_driver acp_pdm_component = {
 366        .name           = DRV_NAME,
 367        .open           = acp_pdm_dma_open,
 368        .close          = acp_pdm_dma_close,
 369        .hw_params      = acp_pdm_dma_hw_params,
 370        .pointer        = acp_pdm_dma_pointer,
 371        .pcm_construct  = acp_pdm_dma_new,
 372};
 373
 374static int acp_pdm_audio_probe(struct platform_device *pdev)
 375{
 376        struct resource *res;
 377        struct pdm_dev_data *adata;
 378        unsigned int irqflags;
 379        int status;
 380
 381        if (!pdev->dev.platform_data) {
 382                dev_err(&pdev->dev, "platform_data not retrieved\n");
 383                return -ENODEV;
 384        }
 385        irqflags = *((unsigned int *)(pdev->dev.platform_data));
 386
 387        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 388        if (!res) {
 389                dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
 390                return -ENODEV;
 391        }
 392
 393        adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);
 394        if (!adata)
 395                return -ENOMEM;
 396
 397        adata->acp_base = devm_ioremap(&pdev->dev, res->start,
 398                                       resource_size(res));
 399        if (!adata->acp_base)
 400                return -ENOMEM;
 401
 402        res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 403        if (!res) {
 404                dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n");
 405                return -ENODEV;
 406        }
 407
 408        adata->pdm_irq = res->start;
 409        adata->capture_stream = NULL;
 410
 411        dev_set_drvdata(&pdev->dev, adata);
 412        status = devm_snd_soc_register_component(&pdev->dev,
 413                                                 &acp_pdm_component,
 414                                                 &acp_pdm_dai_driver, 1);
 415        if (status) {
 416                dev_err(&pdev->dev, "Fail to register acp pdm dai\n");
 417
 418                return -ENODEV;
 419        }
 420        status = devm_request_irq(&pdev->dev, adata->pdm_irq, pdm_irq_handler,
 421                                  irqflags, "ACP_PDM_IRQ", adata);
 422        if (status) {
 423                dev_err(&pdev->dev, "ACP PDM IRQ request failed\n");
 424                return -ENODEV;
 425        }
 426        pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
 427        pm_runtime_use_autosuspend(&pdev->dev);
 428        pm_runtime_enable(&pdev->dev);
 429        pm_runtime_allow(&pdev->dev);
 430        return 0;
 431}
 432
 433static int acp_pdm_audio_remove(struct platform_device *pdev)
 434{
 435        pm_runtime_disable(&pdev->dev);
 436        return 0;
 437}
 438
 439static int acp_pdm_resume(struct device *dev)
 440{
 441        struct pdm_dev_data *adata;
 442        struct snd_pcm_runtime *runtime;
 443        struct pdm_stream_instance *rtd;
 444        u32 period_bytes, buffer_len;
 445
 446        adata = dev_get_drvdata(dev);
 447        if (adata->capture_stream && adata->capture_stream->runtime) {
 448                runtime = adata->capture_stream->runtime;
 449                rtd = runtime->private_data;
 450                period_bytes = frames_to_bytes(runtime, runtime->period_size);
 451                buffer_len = frames_to_bytes(runtime, runtime->buffer_size);
 452                config_acp_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
 453                init_pdm_ring_buffer(MEM_WINDOW_START, buffer_len, period_bytes,
 454                                     adata->acp_base);
 455        }
 456        enable_pdm_interrupts(adata->acp_base);
 457        return 0;
 458}
 459
 460static int acp_pdm_runtime_suspend(struct device *dev)
 461{
 462        struct pdm_dev_data *adata;
 463
 464        adata = dev_get_drvdata(dev);
 465        disable_pdm_interrupts(adata->acp_base);
 466
 467        return 0;
 468}
 469
 470static int acp_pdm_runtime_resume(struct device *dev)
 471{
 472        struct pdm_dev_data *adata;
 473
 474        adata = dev_get_drvdata(dev);
 475        enable_pdm_interrupts(adata->acp_base);
 476        return 0;
 477}
 478
 479static const struct dev_pm_ops acp_pdm_pm_ops = {
 480        .runtime_suspend = acp_pdm_runtime_suspend,
 481        .runtime_resume = acp_pdm_runtime_resume,
 482        .resume = acp_pdm_resume,
 483};
 484
 485static struct platform_driver acp_pdm_dma_driver = {
 486        .probe = acp_pdm_audio_probe,
 487        .remove = acp_pdm_audio_remove,
 488        .driver = {
 489                .name = "acp_rn_pdm_dma",
 490                .pm = &acp_pdm_pm_ops,
 491        },
 492};
 493
 494module_platform_driver(acp_pdm_dma_driver);
 495
 496MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
 497MODULE_DESCRIPTION("AMD ACP3x Renior PDM Driver");
 498MODULE_LICENSE("GPL v2");
 499MODULE_ALIAS("platform:" DRV_NAME);
 500