root/sound/soc/au1x/dma.c

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

DEFINITIONS

This source file includes following definitions.
  1. au1000_release_dma_link
  2. au1000_setup_dma_link
  3. au1000_dma_stop
  4. au1000_dma_start
  5. au1000_dma_interrupt
  6. ss_to_ctx
  7. ss_to_as
  8. alchemy_pcm_open
  9. alchemy_pcm_close
  10. alchemy_pcm_hw_params
  11. alchemy_pcm_hw_free
  12. alchemy_pcm_trigger
  13. alchemy_pcm_pointer
  14. alchemy_pcm_new
  15. alchemy_pcm_drvprobe

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Au1000/Au1500/Au1100 Audio DMA support.
   4  *
   5  * (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com>
   6  *
   7  * copied almost verbatim from the old ALSA driver, written by
   8  *                      Charles Eidsness <charles@cooper-street.com>
   9  */
  10 
  11 #include <linux/module.h>
  12 #include <linux/init.h>
  13 #include <linux/platform_device.h>
  14 #include <linux/slab.h>
  15 #include <linux/dma-mapping.h>
  16 #include <sound/core.h>
  17 #include <sound/pcm.h>
  18 #include <sound/pcm_params.h>
  19 #include <sound/soc.h>
  20 #include <asm/mach-au1x00/au1000.h>
  21 #include <asm/mach-au1x00/au1000_dma.h>
  22 
  23 #include "psc.h"
  24 
  25 #define DRV_NAME "au1x_dma"
  26 
  27 struct pcm_period {
  28         u32 start;
  29         u32 relative_end;       /* relative to start of buffer */
  30         struct pcm_period *next;
  31 };
  32 
  33 struct audio_stream {
  34         struct snd_pcm_substream *substream;
  35         int dma;
  36         struct pcm_period *buffer;
  37         unsigned int period_size;
  38         unsigned int periods;
  39 };
  40 
  41 struct alchemy_pcm_ctx {
  42         struct audio_stream stream[2];  /* playback & capture */
  43 };
  44 
  45 static void au1000_release_dma_link(struct audio_stream *stream)
  46 {
  47         struct pcm_period *pointer;
  48         struct pcm_period *pointer_next;
  49 
  50         stream->period_size = 0;
  51         stream->periods = 0;
  52         pointer = stream->buffer;
  53         if (!pointer)
  54                 return;
  55         do {
  56                 pointer_next = pointer->next;
  57                 kfree(pointer);
  58                 pointer = pointer_next;
  59         } while (pointer != stream->buffer);
  60         stream->buffer = NULL;
  61 }
  62 
  63 static int au1000_setup_dma_link(struct audio_stream *stream,
  64                                  unsigned int period_bytes,
  65                                  unsigned int periods)
  66 {
  67         struct snd_pcm_substream *substream = stream->substream;
  68         struct snd_pcm_runtime *runtime = substream->runtime;
  69         struct pcm_period *pointer;
  70         unsigned long dma_start;
  71         int i;
  72 
  73         dma_start = virt_to_phys(runtime->dma_area);
  74 
  75         if (stream->period_size == period_bytes &&
  76             stream->periods == periods)
  77                 return 0; /* not changed */
  78 
  79         au1000_release_dma_link(stream);
  80 
  81         stream->period_size = period_bytes;
  82         stream->periods = periods;
  83 
  84         stream->buffer = kmalloc(sizeof(struct pcm_period), GFP_KERNEL);
  85         if (!stream->buffer)
  86                 return -ENOMEM;
  87         pointer = stream->buffer;
  88         for (i = 0; i < periods; i++) {
  89                 pointer->start = (u32)(dma_start + (i * period_bytes));
  90                 pointer->relative_end = (u32) (((i+1) * period_bytes) - 0x1);
  91                 if (i < periods - 1) {
  92                         pointer->next = kmalloc(sizeof(struct pcm_period),
  93                                                 GFP_KERNEL);
  94                         if (!pointer->next) {
  95                                 au1000_release_dma_link(stream);
  96                                 return -ENOMEM;
  97                         }
  98                         pointer = pointer->next;
  99                 }
 100         }
 101         pointer->next = stream->buffer;
 102         return 0;
 103 }
 104 
 105 static void au1000_dma_stop(struct audio_stream *stream)
 106 {
 107         if (stream->buffer)
 108                 disable_dma(stream->dma);
 109 }
 110 
 111 static void au1000_dma_start(struct audio_stream *stream)
 112 {
 113         if (!stream->buffer)
 114                 return;
 115 
 116         init_dma(stream->dma);
 117         if (get_dma_active_buffer(stream->dma) == 0) {
 118                 clear_dma_done0(stream->dma);
 119                 set_dma_addr0(stream->dma, stream->buffer->start);
 120                 set_dma_count0(stream->dma, stream->period_size >> 1);
 121                 set_dma_addr1(stream->dma, stream->buffer->next->start);
 122                 set_dma_count1(stream->dma, stream->period_size >> 1);
 123         } else {
 124                 clear_dma_done1(stream->dma);
 125                 set_dma_addr1(stream->dma, stream->buffer->start);
 126                 set_dma_count1(stream->dma, stream->period_size >> 1);
 127                 set_dma_addr0(stream->dma, stream->buffer->next->start);
 128                 set_dma_count0(stream->dma, stream->period_size >> 1);
 129         }
 130         enable_dma_buffers(stream->dma);
 131         start_dma(stream->dma);
 132 }
 133 
 134 static irqreturn_t au1000_dma_interrupt(int irq, void *ptr)
 135 {
 136         struct audio_stream *stream = (struct audio_stream *)ptr;
 137         struct snd_pcm_substream *substream = stream->substream;
 138 
 139         switch (get_dma_buffer_done(stream->dma)) {
 140         case DMA_D0:
 141                 stream->buffer = stream->buffer->next;
 142                 clear_dma_done0(stream->dma);
 143                 set_dma_addr0(stream->dma, stream->buffer->next->start);
 144                 set_dma_count0(stream->dma, stream->period_size >> 1);
 145                 enable_dma_buffer0(stream->dma);
 146                 break;
 147         case DMA_D1:
 148                 stream->buffer = stream->buffer->next;
 149                 clear_dma_done1(stream->dma);
 150                 set_dma_addr1(stream->dma, stream->buffer->next->start);
 151                 set_dma_count1(stream->dma, stream->period_size >> 1);
 152                 enable_dma_buffer1(stream->dma);
 153                 break;
 154         case (DMA_D0 | DMA_D1):
 155                 pr_debug("DMA %d missed interrupt.\n", stream->dma);
 156                 au1000_dma_stop(stream);
 157                 au1000_dma_start(stream);
 158                 break;
 159         case (~DMA_D0 & ~DMA_D1):
 160                 pr_debug("DMA %d empty irq.\n", stream->dma);
 161         }
 162         snd_pcm_period_elapsed(substream);
 163         return IRQ_HANDLED;
 164 }
 165 
 166 static const struct snd_pcm_hardware alchemy_pcm_hardware = {
 167         .info             = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
 168                             SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BATCH,
 169         .period_bytes_min = 1024,
 170         .period_bytes_max = 16 * 1024 - 1,
 171         .periods_min      = 4,
 172         .periods_max      = 255,
 173         .buffer_bytes_max = 128 * 1024,
 174         .fifo_size        = 16,
 175 };
 176 
 177 static inline struct alchemy_pcm_ctx *ss_to_ctx(struct snd_pcm_substream *ss)
 178 {
 179         struct snd_soc_pcm_runtime *rtd = ss->private_data;
 180         struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
 181         return snd_soc_component_get_drvdata(component);
 182 }
 183 
 184 static inline struct audio_stream *ss_to_as(struct snd_pcm_substream *ss)
 185 {
 186         struct alchemy_pcm_ctx *ctx = ss_to_ctx(ss);
 187         return &(ctx->stream[ss->stream]);
 188 }
 189 
 190 static int alchemy_pcm_open(struct snd_pcm_substream *substream)
 191 {
 192         struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream);
 193         struct snd_soc_pcm_runtime *rtd = substream->private_data;
 194         int *dmaids, s = substream->stream;
 195         char *name;
 196 
 197         dmaids = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
 198         if (!dmaids)
 199                 return -ENODEV; /* whoa, has ordering changed? */
 200 
 201         /* DMA setup */
 202         name = (s == SNDRV_PCM_STREAM_PLAYBACK) ? "audio-tx" : "audio-rx";
 203         ctx->stream[s].dma = request_au1000_dma(dmaids[s], name,
 204                                         au1000_dma_interrupt, 0,
 205                                         &ctx->stream[s]);
 206         set_dma_mode(ctx->stream[s].dma,
 207                      get_dma_mode(ctx->stream[s].dma) & ~DMA_NC);
 208 
 209         ctx->stream[s].substream = substream;
 210         ctx->stream[s].buffer = NULL;
 211         snd_soc_set_runtime_hwparams(substream, &alchemy_pcm_hardware);
 212 
 213         return 0;
 214 }
 215 
 216 static int alchemy_pcm_close(struct snd_pcm_substream *substream)
 217 {
 218         struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream);
 219         int stype = substream->stream;
 220 
 221         ctx->stream[stype].substream = NULL;
 222         free_au1000_dma(ctx->stream[stype].dma);
 223 
 224         return 0;
 225 }
 226 
 227 static int alchemy_pcm_hw_params(struct snd_pcm_substream *substream,
 228                                  struct snd_pcm_hw_params *hw_params)
 229 {
 230         struct audio_stream *stream = ss_to_as(substream);
 231         int err;
 232 
 233         err = snd_pcm_lib_malloc_pages(substream,
 234                                        params_buffer_bytes(hw_params));
 235         if (err < 0)
 236                 return err;
 237         err = au1000_setup_dma_link(stream,
 238                                     params_period_bytes(hw_params),
 239                                     params_periods(hw_params));
 240         if (err)
 241                 snd_pcm_lib_free_pages(substream);
 242 
 243         return err;
 244 }
 245 
 246 static int alchemy_pcm_hw_free(struct snd_pcm_substream *substream)
 247 {
 248         struct audio_stream *stream = ss_to_as(substream);
 249         au1000_release_dma_link(stream);
 250         return snd_pcm_lib_free_pages(substream);
 251 }
 252 
 253 static int alchemy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 254 {
 255         struct audio_stream *stream = ss_to_as(substream);
 256         int err = 0;
 257 
 258         switch (cmd) {
 259         case SNDRV_PCM_TRIGGER_START:
 260                 au1000_dma_start(stream);
 261                 break;
 262         case SNDRV_PCM_TRIGGER_STOP:
 263                 au1000_dma_stop(stream);
 264                 break;
 265         default:
 266                 err = -EINVAL;
 267                 break;
 268         }
 269         return err;
 270 }
 271 
 272 static snd_pcm_uframes_t alchemy_pcm_pointer(struct snd_pcm_substream *ss)
 273 {
 274         struct audio_stream *stream = ss_to_as(ss);
 275         long location;
 276 
 277         location = get_dma_residue(stream->dma);
 278         location = stream->buffer->relative_end - location;
 279         if (location == -1)
 280                 location = 0;
 281         return bytes_to_frames(ss->runtime, location);
 282 }
 283 
 284 static const struct snd_pcm_ops alchemy_pcm_ops = {
 285         .open                   = alchemy_pcm_open,
 286         .close                  = alchemy_pcm_close,
 287         .ioctl                  = snd_pcm_lib_ioctl,
 288         .hw_params              = alchemy_pcm_hw_params,
 289         .hw_free                = alchemy_pcm_hw_free,
 290         .trigger                = alchemy_pcm_trigger,
 291         .pointer                = alchemy_pcm_pointer,
 292 };
 293 
 294 static int alchemy_pcm_new(struct snd_soc_pcm_runtime *rtd)
 295 {
 296         struct snd_pcm *pcm = rtd->pcm;
 297 
 298         snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
 299                 snd_dma_continuous_data(GFP_KERNEL), 65536, (4096 * 1024) - 1);
 300 
 301         return 0;
 302 }
 303 
 304 static struct snd_soc_component_driver alchemy_pcm_soc_component = {
 305         .name           = DRV_NAME,
 306         .ops            = &alchemy_pcm_ops,
 307         .pcm_new        = alchemy_pcm_new,
 308 };
 309 
 310 static int alchemy_pcm_drvprobe(struct platform_device *pdev)
 311 {
 312         struct alchemy_pcm_ctx *ctx;
 313 
 314         ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
 315         if (!ctx)
 316                 return -ENOMEM;
 317 
 318         platform_set_drvdata(pdev, ctx);
 319 
 320         return devm_snd_soc_register_component(&pdev->dev,
 321                                         &alchemy_pcm_soc_component, NULL, 0);
 322 }
 323 
 324 static struct platform_driver alchemy_pcmdma_driver = {
 325         .driver = {
 326                 .name   = "alchemy-pcm-dma",
 327         },
 328         .probe          = alchemy_pcm_drvprobe,
 329 };
 330 
 331 module_platform_driver(alchemy_pcmdma_driver);
 332 
 333 MODULE_LICENSE("GPL");
 334 MODULE_DESCRIPTION("Au1000/Au1500/Au1100 Audio DMA driver");
 335 MODULE_AUTHOR("Manuel Lauss");

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