root/sound/soc/kirkwood/kirkwood-dma.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. kirkwood_priv
  2. kirkwood_dma_irq
  3. kirkwood_dma_conf_mbus_windows
  4. kirkwood_dma_open
  5. kirkwood_dma_close
  6. kirkwood_dma_hw_params
  7. kirkwood_dma_hw_free
  8. kirkwood_dma_prepare
  9. kirkwood_dma_pointer
  10. kirkwood_dma_preallocate_dma_buffer
  11. kirkwood_dma_new
  12. kirkwood_dma_free_dma_buffers

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * kirkwood-dma.c
   4  *
   5  * (c) 2010 Arnaud Patard <apatard@mandriva.com>
   6  * (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
   7  */
   8 
   9 #include <linux/init.h>
  10 #include <linux/module.h>
  11 #include <linux/device.h>
  12 #include <linux/io.h>
  13 #include <linux/slab.h>
  14 #include <linux/interrupt.h>
  15 #include <linux/dma-mapping.h>
  16 #include <linux/mbus.h>
  17 #include <sound/soc.h>
  18 #include "kirkwood.h"
  19 
  20 static struct kirkwood_dma_data *kirkwood_priv(struct snd_pcm_substream *subs)
  21 {
  22         struct snd_soc_pcm_runtime *soc_runtime = subs->private_data;
  23         return snd_soc_dai_get_drvdata(soc_runtime->cpu_dai);
  24 }
  25 
  26 static const struct snd_pcm_hardware kirkwood_dma_snd_hw = {
  27         .info = SNDRV_PCM_INFO_INTERLEAVED |
  28                 SNDRV_PCM_INFO_MMAP |
  29                 SNDRV_PCM_INFO_MMAP_VALID |
  30                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
  31                 SNDRV_PCM_INFO_PAUSE |
  32                 SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
  33         .buffer_bytes_max       = KIRKWOOD_SND_MAX_BUFFER_BYTES,
  34         .period_bytes_min       = KIRKWOOD_SND_MIN_PERIOD_BYTES,
  35         .period_bytes_max       = KIRKWOOD_SND_MAX_PERIOD_BYTES,
  36         .periods_min            = KIRKWOOD_SND_MIN_PERIODS,
  37         .periods_max            = KIRKWOOD_SND_MAX_PERIODS,
  38         .fifo_size              = 0,
  39 };
  40 
  41 static irqreturn_t kirkwood_dma_irq(int irq, void *dev_id)
  42 {
  43         struct kirkwood_dma_data *priv = dev_id;
  44         unsigned long mask, status, cause;
  45 
  46         mask = readl(priv->io + KIRKWOOD_INT_MASK);
  47         status = readl(priv->io + KIRKWOOD_INT_CAUSE) & mask;
  48 
  49         cause = readl(priv->io + KIRKWOOD_ERR_CAUSE);
  50         if (unlikely(cause)) {
  51                 printk(KERN_WARNING "%s: got err interrupt 0x%lx\n",
  52                                 __func__, cause);
  53                 writel(cause, priv->io + KIRKWOOD_ERR_CAUSE);
  54         }
  55 
  56         /* we've enabled only bytes interrupts ... */
  57         if (status & ~(KIRKWOOD_INT_CAUSE_PLAY_BYTES | \
  58                         KIRKWOOD_INT_CAUSE_REC_BYTES)) {
  59                 printk(KERN_WARNING "%s: unexpected interrupt %lx\n",
  60                         __func__, status);
  61                 return IRQ_NONE;
  62         }
  63 
  64         /* ack int */
  65         writel(status, priv->io + KIRKWOOD_INT_CAUSE);
  66 
  67         if (status & KIRKWOOD_INT_CAUSE_PLAY_BYTES)
  68                 snd_pcm_period_elapsed(priv->substream_play);
  69 
  70         if (status & KIRKWOOD_INT_CAUSE_REC_BYTES)
  71                 snd_pcm_period_elapsed(priv->substream_rec);
  72 
  73         return IRQ_HANDLED;
  74 }
  75 
  76 static void
  77 kirkwood_dma_conf_mbus_windows(void __iomem *base, int win,
  78                                unsigned long dma,
  79                                const struct mbus_dram_target_info *dram)
  80 {
  81         int i;
  82 
  83         /* First disable and clear windows */
  84         writel(0, base + KIRKWOOD_AUDIO_WIN_CTRL_REG(win));
  85         writel(0, base + KIRKWOOD_AUDIO_WIN_BASE_REG(win));
  86 
  87         /* try to find matching cs for current dma address */
  88         for (i = 0; i < dram->num_cs; i++) {
  89                 const struct mbus_dram_window *cs = dram->cs + i;
  90                 if ((cs->base & 0xffff0000) < (dma & 0xffff0000)) {
  91                         writel(cs->base & 0xffff0000,
  92                                 base + KIRKWOOD_AUDIO_WIN_BASE_REG(win));
  93                         writel(((cs->size - 1) & 0xffff0000) |
  94                                 (cs->mbus_attr << 8) |
  95                                 (dram->mbus_dram_target_id << 4) | 1,
  96                                 base + KIRKWOOD_AUDIO_WIN_CTRL_REG(win));
  97                 }
  98         }
  99 }
 100 
 101 static int kirkwood_dma_open(struct snd_pcm_substream *substream)
 102 {
 103         int err;
 104         struct snd_pcm_runtime *runtime = substream->runtime;
 105         struct kirkwood_dma_data *priv = kirkwood_priv(substream);
 106         const struct mbus_dram_target_info *dram;
 107         unsigned long addr;
 108 
 109         snd_soc_set_runtime_hwparams(substream, &kirkwood_dma_snd_hw);
 110 
 111         /* Ensure that all constraints linked to dma burst are fulfilled */
 112         err = snd_pcm_hw_constraint_minmax(runtime,
 113                         SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
 114                         priv->burst * 2,
 115                         KIRKWOOD_AUDIO_BUF_MAX-1);
 116         if (err < 0)
 117                 return err;
 118 
 119         err = snd_pcm_hw_constraint_step(runtime, 0,
 120                         SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
 121                         priv->burst);
 122         if (err < 0)
 123                 return err;
 124 
 125         err = snd_pcm_hw_constraint_step(substream->runtime, 0,
 126                          SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
 127                          priv->burst);
 128         if (err < 0)
 129                 return err;
 130 
 131         if (!priv->substream_play && !priv->substream_rec) {
 132                 err = request_irq(priv->irq, kirkwood_dma_irq, IRQF_SHARED,
 133                                   "kirkwood-i2s", priv);
 134                 if (err)
 135                         return -EBUSY;
 136 
 137                 /*
 138                  * Enable Error interrupts. We're only ack'ing them but
 139                  * it's useful for diagnostics
 140                  */
 141                 writel((unsigned int)-1, priv->io + KIRKWOOD_ERR_MASK);
 142         }
 143 
 144         dram = mv_mbus_dram_info();
 145         addr = substream->dma_buffer.addr;
 146         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 147                 if (priv->substream_play)
 148                         return -EBUSY;
 149                 priv->substream_play = substream;
 150                 kirkwood_dma_conf_mbus_windows(priv->io,
 151                         KIRKWOOD_PLAYBACK_WIN, addr, dram);
 152         } else {
 153                 if (priv->substream_rec)
 154                         return -EBUSY;
 155                 priv->substream_rec = substream;
 156                 kirkwood_dma_conf_mbus_windows(priv->io,
 157                         KIRKWOOD_RECORD_WIN, addr, dram);
 158         }
 159 
 160         return 0;
 161 }
 162 
 163 static int kirkwood_dma_close(struct snd_pcm_substream *substream)
 164 {
 165         struct kirkwood_dma_data *priv = kirkwood_priv(substream);
 166 
 167         if (!priv)
 168                 return 0;
 169 
 170         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 171                 priv->substream_play = NULL;
 172         else
 173                 priv->substream_rec = NULL;
 174 
 175         if (!priv->substream_play && !priv->substream_rec) {
 176                 writel(0, priv->io + KIRKWOOD_ERR_MASK);
 177                 free_irq(priv->irq, priv);
 178         }
 179 
 180         return 0;
 181 }
 182 
 183 static int kirkwood_dma_hw_params(struct snd_pcm_substream *substream,
 184                                 struct snd_pcm_hw_params *params)
 185 {
 186         struct snd_pcm_runtime *runtime = substream->runtime;
 187 
 188         snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
 189         runtime->dma_bytes = params_buffer_bytes(params);
 190 
 191         return 0;
 192 }
 193 
 194 static int kirkwood_dma_hw_free(struct snd_pcm_substream *substream)
 195 {
 196         snd_pcm_set_runtime_buffer(substream, NULL);
 197         return 0;
 198 }
 199 
 200 static int kirkwood_dma_prepare(struct snd_pcm_substream *substream)
 201 {
 202         struct snd_pcm_runtime *runtime = substream->runtime;
 203         struct kirkwood_dma_data *priv = kirkwood_priv(substream);
 204         unsigned long size, count;
 205 
 206         /* compute buffer size in term of "words" as requested in specs */
 207         size = frames_to_bytes(runtime, runtime->buffer_size);
 208         size = (size>>2)-1;
 209         count = snd_pcm_lib_period_bytes(substream);
 210 
 211         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 212                 writel(count, priv->io + KIRKWOOD_PLAY_BYTE_INT_COUNT);
 213                 writel(runtime->dma_addr, priv->io + KIRKWOOD_PLAY_BUF_ADDR);
 214                 writel(size, priv->io + KIRKWOOD_PLAY_BUF_SIZE);
 215         } else {
 216                 writel(count, priv->io + KIRKWOOD_REC_BYTE_INT_COUNT);
 217                 writel(runtime->dma_addr, priv->io + KIRKWOOD_REC_BUF_ADDR);
 218                 writel(size, priv->io + KIRKWOOD_REC_BUF_SIZE);
 219         }
 220 
 221 
 222         return 0;
 223 }
 224 
 225 static snd_pcm_uframes_t kirkwood_dma_pointer(struct snd_pcm_substream
 226                                                 *substream)
 227 {
 228         struct kirkwood_dma_data *priv = kirkwood_priv(substream);
 229         snd_pcm_uframes_t count;
 230 
 231         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 232                 count = bytes_to_frames(substream->runtime,
 233                         readl(priv->io + KIRKWOOD_PLAY_BYTE_COUNT));
 234         else
 235                 count = bytes_to_frames(substream->runtime,
 236                         readl(priv->io + KIRKWOOD_REC_BYTE_COUNT));
 237 
 238         return count;
 239 }
 240 
 241 static const struct snd_pcm_ops kirkwood_dma_ops = {
 242         .open =         kirkwood_dma_open,
 243         .close =        kirkwood_dma_close,
 244         .ioctl =        snd_pcm_lib_ioctl,
 245         .hw_params =    kirkwood_dma_hw_params,
 246         .hw_free =      kirkwood_dma_hw_free,
 247         .prepare =      kirkwood_dma_prepare,
 248         .pointer =      kirkwood_dma_pointer,
 249 };
 250 
 251 static int kirkwood_dma_preallocate_dma_buffer(struct snd_pcm *pcm,
 252                 int stream)
 253 {
 254         struct snd_pcm_substream *substream = pcm->streams[stream].substream;
 255         struct snd_dma_buffer *buf = &substream->dma_buffer;
 256         size_t size = kirkwood_dma_snd_hw.buffer_bytes_max;
 257 
 258         buf->dev.type = SNDRV_DMA_TYPE_DEV;
 259         buf->dev.dev = pcm->card->dev;
 260         buf->area = dma_alloc_coherent(pcm->card->dev, size,
 261                         &buf->addr, GFP_KERNEL);
 262         if (!buf->area)
 263                 return -ENOMEM;
 264         buf->bytes = size;
 265         buf->private_data = NULL;
 266 
 267         return 0;
 268 }
 269 
 270 static int kirkwood_dma_new(struct snd_soc_pcm_runtime *rtd)
 271 {
 272         struct snd_card *card = rtd->card->snd_card;
 273         struct snd_pcm *pcm = rtd->pcm;
 274         int ret;
 275 
 276         ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
 277         if (ret)
 278                 return ret;
 279 
 280         if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
 281                 ret = kirkwood_dma_preallocate_dma_buffer(pcm,
 282                                 SNDRV_PCM_STREAM_PLAYBACK);
 283                 if (ret)
 284                         return ret;
 285         }
 286 
 287         if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
 288                 ret = kirkwood_dma_preallocate_dma_buffer(pcm,
 289                                 SNDRV_PCM_STREAM_CAPTURE);
 290                 if (ret)
 291                         return ret;
 292         }
 293 
 294         return 0;
 295 }
 296 
 297 static void kirkwood_dma_free_dma_buffers(struct snd_pcm *pcm)
 298 {
 299         struct snd_pcm_substream *substream;
 300         struct snd_dma_buffer *buf;
 301         int stream;
 302 
 303         for (stream = 0; stream < 2; stream++) {
 304                 substream = pcm->streams[stream].substream;
 305                 if (!substream)
 306                         continue;
 307                 buf = &substream->dma_buffer;
 308                 if (!buf->area)
 309                         continue;
 310 
 311                 dma_free_coherent(pcm->card->dev, buf->bytes,
 312                                 buf->area, buf->addr);
 313                 buf->area = NULL;
 314         }
 315 }
 316 
 317 const struct snd_soc_component_driver kirkwood_soc_component = {
 318         .name           = DRV_NAME,
 319         .ops            = &kirkwood_dma_ops,
 320         .pcm_new        = kirkwood_dma_new,
 321         .pcm_free       = kirkwood_dma_free_dma_buffers,
 322 };

/* [<][>][^][v][top][bottom][index][help] */