linux/sound/soc/meson/aiu-fifo.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2//
   3// Copyright (c) 2020 BayLibre, SAS.
   4// Author: Jerome Brunet <jbrunet@baylibre.com>
   5
   6#include <linux/bitfield.h>
   7#include <linux/clk.h>
   8#include <sound/pcm_params.h>
   9#include <sound/soc.h>
  10#include <sound/soc-dai.h>
  11
  12#include "aiu-fifo.h"
  13
  14#define AIU_MEM_START   0x00
  15#define AIU_MEM_RD      0x04
  16#define AIU_MEM_END     0x08
  17#define AIU_MEM_MASKS   0x0c
  18#define  AIU_MEM_MASK_CH_RD GENMASK(7, 0)
  19#define  AIU_MEM_MASK_CH_MEM GENMASK(15, 8)
  20#define AIU_MEM_CONTROL 0x10
  21#define  AIU_MEM_CONTROL_INIT BIT(0)
  22#define  AIU_MEM_CONTROL_FILL_EN BIT(1)
  23#define  AIU_MEM_CONTROL_EMPTY_EN BIT(2)
  24
  25static struct snd_soc_dai *aiu_fifo_dai(struct snd_pcm_substream *ss)
  26{
  27        struct snd_soc_pcm_runtime *rtd = ss->private_data;
  28
  29        return asoc_rtd_to_cpu(rtd, 0);
  30}
  31
  32snd_pcm_uframes_t aiu_fifo_pointer(struct snd_soc_component *component,
  33                                   struct snd_pcm_substream *substream)
  34{
  35        struct snd_soc_dai *dai = aiu_fifo_dai(substream);
  36        struct aiu_fifo *fifo = dai->playback_dma_data;
  37        struct snd_pcm_runtime *runtime = substream->runtime;
  38        unsigned int addr;
  39
  40        addr = snd_soc_component_read(component, fifo->mem_offset + AIU_MEM_RD);
  41
  42        return bytes_to_frames(runtime, addr - (unsigned int)runtime->dma_addr);
  43}
  44
  45static void aiu_fifo_enable(struct snd_soc_dai *dai, bool enable)
  46{
  47        struct snd_soc_component *component = dai->component;
  48        struct aiu_fifo *fifo = dai->playback_dma_data;
  49        unsigned int en_mask = (AIU_MEM_CONTROL_FILL_EN |
  50                                AIU_MEM_CONTROL_EMPTY_EN);
  51
  52        snd_soc_component_update_bits(component,
  53                                      fifo->mem_offset + AIU_MEM_CONTROL,
  54                                      en_mask, enable ? en_mask : 0);
  55}
  56
  57int aiu_fifo_trigger(struct snd_pcm_substream *substream, int cmd,
  58                     struct snd_soc_dai *dai)
  59{
  60        switch (cmd) {
  61        case SNDRV_PCM_TRIGGER_START:
  62        case SNDRV_PCM_TRIGGER_RESUME:
  63        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
  64                aiu_fifo_enable(dai, true);
  65                break;
  66        case SNDRV_PCM_TRIGGER_SUSPEND:
  67        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
  68        case SNDRV_PCM_TRIGGER_STOP:
  69                aiu_fifo_enable(dai, false);
  70                break;
  71        default:
  72                return -EINVAL;
  73        }
  74
  75        return 0;
  76}
  77
  78int aiu_fifo_prepare(struct snd_pcm_substream *substream,
  79                     struct snd_soc_dai *dai)
  80{
  81        struct snd_soc_component *component = dai->component;
  82        struct aiu_fifo *fifo = dai->playback_dma_data;
  83
  84        snd_soc_component_update_bits(component,
  85                                      fifo->mem_offset + AIU_MEM_CONTROL,
  86                                      AIU_MEM_CONTROL_INIT,
  87                                      AIU_MEM_CONTROL_INIT);
  88        snd_soc_component_update_bits(component,
  89                                      fifo->mem_offset + AIU_MEM_CONTROL,
  90                                      AIU_MEM_CONTROL_INIT, 0);
  91        return 0;
  92}
  93
  94int aiu_fifo_hw_params(struct snd_pcm_substream *substream,
  95                       struct snd_pcm_hw_params *params,
  96                       struct snd_soc_dai *dai)
  97{
  98        struct snd_pcm_runtime *runtime = substream->runtime;
  99        struct snd_soc_component *component = dai->component;
 100        struct aiu_fifo *fifo = dai->playback_dma_data;
 101        dma_addr_t end;
 102
 103        /* Setup the fifo boundaries */
 104        end = runtime->dma_addr + runtime->dma_bytes - fifo->fifo_block;
 105        snd_soc_component_write(component, fifo->mem_offset + AIU_MEM_START,
 106                                runtime->dma_addr);
 107        snd_soc_component_write(component, fifo->mem_offset + AIU_MEM_RD,
 108                                runtime->dma_addr);
 109        snd_soc_component_write(component, fifo->mem_offset + AIU_MEM_END,
 110                                end);
 111
 112        /* Setup the fifo to read all the memory - no skip */
 113        snd_soc_component_update_bits(component,
 114                                      fifo->mem_offset + AIU_MEM_MASKS,
 115                                      AIU_MEM_MASK_CH_RD | AIU_MEM_MASK_CH_MEM,
 116                                      FIELD_PREP(AIU_MEM_MASK_CH_RD, 0xff) |
 117                                      FIELD_PREP(AIU_MEM_MASK_CH_MEM, 0xff));
 118
 119        return 0;
 120}
 121
 122static irqreturn_t aiu_fifo_isr(int irq, void *dev_id)
 123{
 124        struct snd_pcm_substream *playback = dev_id;
 125
 126        snd_pcm_period_elapsed(playback);
 127
 128        return IRQ_HANDLED;
 129}
 130
 131int aiu_fifo_startup(struct snd_pcm_substream *substream,
 132                     struct snd_soc_dai *dai)
 133{
 134        struct aiu_fifo *fifo = dai->playback_dma_data;
 135        int ret;
 136
 137        snd_soc_set_runtime_hwparams(substream, fifo->pcm);
 138
 139        /*
 140         * Make sure the buffer and period size are multiple of the fifo burst
 141         * size
 142         */
 143        ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
 144                                         SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
 145                                         fifo->fifo_block);
 146        if (ret)
 147                return ret;
 148
 149        ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
 150                                         SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
 151                                         fifo->fifo_block);
 152        if (ret)
 153                return ret;
 154
 155        ret = clk_prepare_enable(fifo->pclk);
 156        if (ret)
 157                return ret;
 158
 159        ret = request_irq(fifo->irq, aiu_fifo_isr, 0, dev_name(dai->dev),
 160                          substream);
 161        if (ret)
 162                clk_disable_unprepare(fifo->pclk);
 163
 164        return ret;
 165}
 166
 167void aiu_fifo_shutdown(struct snd_pcm_substream *substream,
 168                       struct snd_soc_dai *dai)
 169{
 170        struct aiu_fifo *fifo = dai->playback_dma_data;
 171
 172        free_irq(fifo->irq, substream);
 173        clk_disable_unprepare(fifo->pclk);
 174}
 175
 176int aiu_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd,
 177                     struct snd_soc_dai *dai)
 178{
 179        struct snd_card *card = rtd->card->snd_card;
 180        struct aiu_fifo *fifo = dai->playback_dma_data;
 181        size_t size = fifo->pcm->buffer_bytes_max;
 182
 183        snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
 184                                       card->dev, size, size);
 185
 186        return 0;
 187}
 188
 189int aiu_fifo_dai_probe(struct snd_soc_dai *dai)
 190{
 191        struct aiu_fifo *fifo;
 192
 193        fifo = kzalloc(sizeof(*fifo), GFP_KERNEL);
 194        if (!fifo)
 195                return -ENOMEM;
 196
 197        dai->playback_dma_data = fifo;
 198
 199        return 0;
 200}
 201
 202int aiu_fifo_dai_remove(struct snd_soc_dai *dai)
 203{
 204        kfree(dai->playback_dma_data);
 205
 206        return 0;
 207}
 208
 209