root/sound/soc/sh/dma-sh7760.c

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

DEFINITIONS

This source file includes following definitions.
  1. camelot_txdma
  2. camelot_rxdma
  3. camelot_pcm_open
  4. camelot_pcm_close
  5. camelot_hw_params
  6. camelot_hw_free
  7. camelot_prepare
  8. dmabrg_play_dma_start
  9. dmabrg_play_dma_stop
  10. dmabrg_rec_dma_start
  11. dmabrg_rec_dma_stop
  12. camelot_trigger
  13. camelot_pos
  14. camelot_pcm_new
  15. sh7760_soc_platform_probe

   1 // SPDX-License-Identifier: GPL-2.0
   2 //
   3 // SH7760 ("camelot") DMABRG audio DMA unit support
   4 //
   5 // Copyright (C) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
   6 //
   7 // The SH7760 DMABRG provides 4 dma channels (2x rec, 2x play), which
   8 // trigger an interrupt when one half of the programmed transfer size
   9 // has been xmitted.
  10 //
  11 // FIXME: little-endian only for now
  12 
  13 #include <linux/module.h>
  14 #include <linux/gfp.h>
  15 #include <linux/init.h>
  16 #include <linux/platform_device.h>
  17 #include <linux/dma-mapping.h>
  18 #include <sound/core.h>
  19 #include <sound/pcm.h>
  20 #include <sound/pcm_params.h>
  21 #include <sound/soc.h>
  22 #include <asm/dmabrg.h>
  23 
  24 
  25 /* registers and bits */
  26 #define BRGATXSAR       0x00
  27 #define BRGARXDAR       0x04
  28 #define BRGATXTCR       0x08
  29 #define BRGARXTCR       0x0C
  30 #define BRGACR          0x10
  31 #define BRGATXTCNT      0x14
  32 #define BRGARXTCNT      0x18
  33 
  34 #define ACR_RAR         (1 << 18)
  35 #define ACR_RDS         (1 << 17)
  36 #define ACR_RDE         (1 << 16)
  37 #define ACR_TAR         (1 << 2)
  38 #define ACR_TDS         (1 << 1)
  39 #define ACR_TDE         (1 << 0)
  40 
  41 /* receiver/transmitter data alignment */
  42 #define ACR_RAM_NONE    (0 << 24)
  43 #define ACR_RAM_4BYTE   (1 << 24)
  44 #define ACR_RAM_2WORD   (2 << 24)
  45 #define ACR_TAM_NONE    (0 << 8)
  46 #define ACR_TAM_4BYTE   (1 << 8)
  47 #define ACR_TAM_2WORD   (2 << 8)
  48 
  49 
  50 struct camelot_pcm {
  51         unsigned long mmio;  /* DMABRG audio channel control reg MMIO */
  52         unsigned int txid;    /* ID of first DMABRG IRQ for this unit */
  53 
  54         struct snd_pcm_substream *tx_ss;
  55         unsigned long tx_period_size;
  56         unsigned int  tx_period;
  57 
  58         struct snd_pcm_substream *rx_ss;
  59         unsigned long rx_period_size;
  60         unsigned int  rx_period;
  61 
  62 } cam_pcm_data[2] = {
  63         {
  64                 .mmio   =       0xFE3C0040,
  65                 .txid   =       DMABRGIRQ_A0TXF,
  66         },
  67         {
  68                 .mmio   =       0xFE3C0060,
  69                 .txid   =       DMABRGIRQ_A1TXF,
  70         },
  71 };
  72 
  73 #define BRGREG(x)       (*(unsigned long *)(cam->mmio + (x)))
  74 
  75 /*
  76  * set a minimum of 16kb per period, to avoid interrupt-"storm" and
  77  * resulting skipping. In general, the bigger the minimum size, the
  78  * better for overall system performance. (The SH7760 is a puny CPU
  79  * with a slow SDRAM interface and poor internal bus bandwidth,
  80  * *especially* when the LCDC is active).  The minimum for the DMAC
  81  * is 8 bytes; 16kbytes are enough to get skip-free playback of a
  82  * 44kHz/16bit/stereo MP3 on a lightly loaded system, and maintain
  83  * reasonable responsiveness in MPlayer.
  84  */
  85 #define DMABRG_PERIOD_MIN               16 * 1024
  86 #define DMABRG_PERIOD_MAX               0x03fffffc
  87 #define DMABRG_PREALLOC_BUFFER          32 * 1024
  88 #define DMABRG_PREALLOC_BUFFER_MAX      32 * 1024
  89 
  90 static const struct snd_pcm_hardware camelot_pcm_hardware = {
  91         .info = (SNDRV_PCM_INFO_MMAP |
  92                 SNDRV_PCM_INFO_INTERLEAVED |
  93                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
  94                 SNDRV_PCM_INFO_MMAP_VALID |
  95                 SNDRV_PCM_INFO_BATCH),
  96         .buffer_bytes_max =     DMABRG_PERIOD_MAX,
  97         .period_bytes_min =     DMABRG_PERIOD_MIN,
  98         .period_bytes_max =     DMABRG_PERIOD_MAX / 2,
  99         .periods_min =          2,
 100         .periods_max =          2,
 101         .fifo_size =            128,
 102 };
 103 
 104 static void camelot_txdma(void *data)
 105 {
 106         struct camelot_pcm *cam = data;
 107         cam->tx_period ^= 1;
 108         snd_pcm_period_elapsed(cam->tx_ss);
 109 }
 110 
 111 static void camelot_rxdma(void *data)
 112 {
 113         struct camelot_pcm *cam = data;
 114         cam->rx_period ^= 1;
 115         snd_pcm_period_elapsed(cam->rx_ss);
 116 }
 117 
 118 static int camelot_pcm_open(struct snd_pcm_substream *substream)
 119 {
 120         struct snd_soc_pcm_runtime *rtd = substream->private_data;
 121         struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id];
 122         int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
 123         int ret, dmairq;
 124 
 125         snd_soc_set_runtime_hwparams(substream, &camelot_pcm_hardware);
 126 
 127         /* DMABRG buffer half/full events */
 128         dmairq = (recv) ? cam->txid + 2 : cam->txid;
 129         if (recv) {
 130                 cam->rx_ss = substream;
 131                 ret = dmabrg_request_irq(dmairq, camelot_rxdma, cam);
 132                 if (unlikely(ret)) {
 133                         pr_debug("audio unit %d irqs already taken!\n",
 134                              rtd->cpu_dai->id);
 135                         return -EBUSY;
 136                 }
 137                 (void)dmabrg_request_irq(dmairq + 1,camelot_rxdma, cam);
 138         } else {
 139                 cam->tx_ss = substream;
 140                 ret = dmabrg_request_irq(dmairq, camelot_txdma, cam);
 141                 if (unlikely(ret)) {
 142                         pr_debug("audio unit %d irqs already taken!\n",
 143                              rtd->cpu_dai->id);
 144                         return -EBUSY;
 145                 }
 146                 (void)dmabrg_request_irq(dmairq + 1, camelot_txdma, cam);
 147         }
 148         return 0;
 149 }
 150 
 151 static int camelot_pcm_close(struct snd_pcm_substream *substream)
 152 {
 153         struct snd_soc_pcm_runtime *rtd = substream->private_data;
 154         struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id];
 155         int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
 156         int dmairq;
 157 
 158         dmairq = (recv) ? cam->txid + 2 : cam->txid;
 159 
 160         if (recv)
 161                 cam->rx_ss = NULL;
 162         else
 163                 cam->tx_ss = NULL;
 164 
 165         dmabrg_free_irq(dmairq + 1);
 166         dmabrg_free_irq(dmairq);
 167 
 168         return 0;
 169 }
 170 
 171 static int camelot_hw_params(struct snd_pcm_substream *substream,
 172                              struct snd_pcm_hw_params *hw_params)
 173 {
 174         struct snd_soc_pcm_runtime *rtd = substream->private_data;
 175         struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id];
 176         int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
 177         int ret;
 178 
 179         ret = snd_pcm_lib_malloc_pages(substream,
 180                                        params_buffer_bytes(hw_params));
 181         if (ret < 0)
 182                 return ret;
 183 
 184         if (recv) {
 185                 cam->rx_period_size = params_period_bytes(hw_params);
 186                 cam->rx_period = 0;
 187         } else {
 188                 cam->tx_period_size = params_period_bytes(hw_params);
 189                 cam->tx_period = 0;
 190         }
 191         return 0;
 192 }
 193 
 194 static int camelot_hw_free(struct snd_pcm_substream *substream)
 195 {
 196         return snd_pcm_lib_free_pages(substream);
 197 }
 198 
 199 static int camelot_prepare(struct snd_pcm_substream *substream)
 200 {
 201         struct snd_pcm_runtime *runtime = substream->runtime;
 202         struct snd_soc_pcm_runtime *rtd = substream->private_data;
 203         struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id];
 204 
 205         pr_debug("PCM data: addr 0x%08lx len %d\n",
 206                  (u32)runtime->dma_addr, runtime->dma_bytes);
 207  
 208         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 209                 BRGREG(BRGATXSAR) = (unsigned long)runtime->dma_area;
 210                 BRGREG(BRGATXTCR) = runtime->dma_bytes;
 211         } else {
 212                 BRGREG(BRGARXDAR) = (unsigned long)runtime->dma_area;
 213                 BRGREG(BRGARXTCR) = runtime->dma_bytes;
 214         }
 215 
 216         return 0;
 217 }
 218 
 219 static inline void dmabrg_play_dma_start(struct camelot_pcm *cam)
 220 {
 221         unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
 222         /* start DMABRG engine: XFER start, auto-addr-reload */
 223         BRGREG(BRGACR) = acr | ACR_TDE | ACR_TAR | ACR_TAM_2WORD;
 224 }
 225 
 226 static inline void dmabrg_play_dma_stop(struct camelot_pcm *cam)
 227 {
 228         unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
 229         /* forcibly terminate data transmission */
 230         BRGREG(BRGACR) = acr | ACR_TDS;
 231 }
 232 
 233 static inline void dmabrg_rec_dma_start(struct camelot_pcm *cam)
 234 {
 235         unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
 236         /* start DMABRG engine: recv start, auto-reload */
 237         BRGREG(BRGACR) = acr | ACR_RDE | ACR_RAR | ACR_RAM_2WORD;
 238 }
 239 
 240 static inline void dmabrg_rec_dma_stop(struct camelot_pcm *cam)
 241 {
 242         unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
 243         /* forcibly terminate data receiver */
 244         BRGREG(BRGACR) = acr | ACR_RDS;
 245 }
 246 
 247 static int camelot_trigger(struct snd_pcm_substream *substream, int cmd)
 248 {
 249         struct snd_soc_pcm_runtime *rtd = substream->private_data;
 250         struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id];
 251         int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
 252 
 253         switch (cmd) {
 254         case SNDRV_PCM_TRIGGER_START:
 255                 if (recv)
 256                         dmabrg_rec_dma_start(cam);
 257                 else
 258                         dmabrg_play_dma_start(cam);
 259                 break;
 260         case SNDRV_PCM_TRIGGER_STOP:
 261                 if (recv)
 262                         dmabrg_rec_dma_stop(cam);
 263                 else
 264                         dmabrg_play_dma_stop(cam);
 265                 break;
 266         default:
 267                 return -EINVAL;
 268         }
 269 
 270         return 0;
 271 }
 272 
 273 static snd_pcm_uframes_t camelot_pos(struct snd_pcm_substream *substream)
 274 {
 275         struct snd_pcm_runtime *runtime = substream->runtime;
 276         struct snd_soc_pcm_runtime *rtd = substream->private_data;
 277         struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id];
 278         int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
 279         unsigned long pos;
 280 
 281         /* cannot use the DMABRG pointer register: under load, by the
 282          * time ALSA comes around to read the register, it is already
 283          * far ahead (or worse, already done with the fragment) of the
 284          * position at the time the IRQ was triggered, which results in
 285          * fast-playback sound in my test application (ScummVM)
 286          */
 287         if (recv)
 288                 pos = cam->rx_period ? cam->rx_period_size : 0;
 289         else
 290                 pos = cam->tx_period ? cam->tx_period_size : 0;
 291 
 292         return bytes_to_frames(runtime, pos);
 293 }
 294 
 295 static const struct snd_pcm_ops camelot_pcm_ops = {
 296         .open           = camelot_pcm_open,
 297         .close          = camelot_pcm_close,
 298         .ioctl          = snd_pcm_lib_ioctl,
 299         .hw_params      = camelot_hw_params,
 300         .hw_free        = camelot_hw_free,
 301         .prepare        = camelot_prepare,
 302         .trigger        = camelot_trigger,
 303         .pointer        = camelot_pos,
 304 };
 305 
 306 static int camelot_pcm_new(struct snd_soc_pcm_runtime *rtd)
 307 {
 308         struct snd_pcm *pcm = rtd->pcm;
 309 
 310         /* dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel
 311          * in MMAP mode (i.e. aplay -M)
 312          */
 313         snd_pcm_lib_preallocate_pages_for_all(pcm,
 314                 SNDRV_DMA_TYPE_CONTINUOUS,
 315                 snd_dma_continuous_data(GFP_KERNEL),
 316                 DMABRG_PREALLOC_BUFFER, DMABRG_PREALLOC_BUFFER_MAX);
 317 
 318         return 0;
 319 }
 320 
 321 static const struct snd_soc_component_driver sh7760_soc_component = {
 322         .ops            = &camelot_pcm_ops,
 323         .pcm_new        = camelot_pcm_new,
 324 };
 325 
 326 static int sh7760_soc_platform_probe(struct platform_device *pdev)
 327 {
 328         return devm_snd_soc_register_component(&pdev->dev, &sh7760_soc_component,
 329                                                NULL, 0);
 330 }
 331 
 332 static struct platform_driver sh7760_pcm_driver = {
 333         .driver = {
 334                         .name = "sh7760-pcm-audio",
 335         },
 336 
 337         .probe = sh7760_soc_platform_probe,
 338 };
 339 
 340 module_platform_driver(sh7760_pcm_driver);
 341 
 342 MODULE_LICENSE("GPL v2");
 343 MODULE_DESCRIPTION("SH7760 Audio DMA (DMABRG) driver");
 344 MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");

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