AS3525: Implement a true audio pause and full-resolution audio tick. Take care of...
[kugel-rb.git] / firmware / target / arm / as3525 / dma-pl081.c
blobf4cc07df303b505c8e62375cc2ec9fbdb3151dcc
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright © 2008 Rafaël Carré
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include "config.h"
23 #include "system.h"
24 #include "pl081.h"
25 #include "dma-target.h"
26 #include "panic.h"
28 static int dma_used = 0;
29 static void (*dma_callback[2])(void); /* 2 channels */
31 void dma_retain(void)
33 if(++dma_used == 1)
35 bitset32(&CGU_PERI, CGU_DMA_CLOCK_ENABLE);
36 bitset32(&DMAC_CONFIGURATION, 1<<0);
40 void dma_release(void)
42 if(--dma_used == 0)
44 bitclr32(&DMAC_CONFIGURATION, 1<<0);
45 bitclr32(&CGU_PERI, CGU_DMA_CLOCK_ENABLE);
47 if (dma_used < 0)
48 panicf("dma_used < 0!");
51 void dma_init(void)
53 #if CONFIG_CPU == AS3525
54 DMAC_SYNC = 0xffff; /* disable synchronisation logic */
55 #endif
56 VIC_INT_ENABLE = INTERRUPT_DMAC;
59 void dma_pause_channel(int channel)
61 /* Disable the channel - clears the FIFO after sending last word */
62 bitclr32(&DMAC_CH_CONFIGURATION(channel), 1<<0);
63 /* Wait for it to go inactive */
64 while (DMAC_CH_CONFIGURATION(channel) & (1<<17));
67 void dma_resume_channel(int channel)
69 /* Resume - must reinit to where it left off (so the docs say) */
70 unsigned long control = DMAC_CH_CONTROL(channel);
71 if ((control & 0x7ff) == 0)
72 return; /* empty */
74 DMAC_INT_TC_CLEAR = (1<<channel);
75 DMAC_INT_ERR_CLEAR = (1<<channel);
76 DMAC_CH_SRC_ADDR(channel) = DMAC_CH_SRC_ADDR(channel);
77 DMAC_CH_DST_ADDR(channel) = DMAC_CH_DST_ADDR(channel);
78 DMAC_CH_LLI(channel) = DMAC_CH_LLI(channel);
79 DMAC_CH_CONTROL(channel) = control;
80 bitset32(&DMAC_CH_CONFIGURATION(channel), (1<<0));
83 void dma_disable_channel(int channel) \
84 __attribute__((alias("dma_pause_channel")));
86 void dma_enable_channel(int channel, void *src, void *dst, int peri,
87 int flow_controller, bool src_inc, bool dst_inc,
88 size_t size, int nwords, void (*callback)(void))
90 dma_callback[channel] = callback;
92 /* Clear any pending interrupts leftover from previous operation */
93 DMAC_INT_TC_CLEAR = (1<<channel);
94 DMAC_INT_ERR_CLEAR = (1<<channel);
96 DMAC_CH_SRC_ADDR(channel) = (int)src;
97 DMAC_CH_DST_ADDR(channel) = (int)dst;
99 /* When LLI is 0 channel is disabled upon transfer completion */
100 DMAC_CH_LLI(channel) = 0;
102 /* Channel Control Register */
103 DMAC_CH_CONTROL(channel) =
104 ((1<<31) /* LLI triggers terminal count interrupt */
105 /* | (1<<30) */ /* cacheable = 1, non = 0 */
106 /* | (1<<29) */ /* bufferable = 1, non = 0 */
107 /* | (1<<28) */ /* privileged = 1, user = 0 */
108 | (dst_inc? (1<<27): 0) /* specify address increment */
109 | (src_inc? (1<<26): 0) /* specify address increment */
110 /* [25:24] */ /* undefined */
111 | (2<<21) /* dst width = word, 32bit */
112 | (2<<18) /* src width = word, 32bit */
113 /* OF uses transfers of 4 * 32 bits words on memory, i2sin, i2sout */
114 /* OF uses transfers of 8 * 32 bits words on SD */
115 | (nwords<<15) /* dst size */
116 | (nwords<<12) /* src size */
117 | ((size & 0x7ff)<<0)); /* transfer size */
119 /* Channel Config Register */
120 DMAC_CH_CONFIGURATION(channel) =
121 /* [31:19] */ /* Read undefined. Write as zero */
122 /* (0<<18) */ /* Halt Bit */
123 /* (0<<17) */ /* Active Bit */
124 /* (0<<16) */ /* Lock Bit */
125 (1<<15) /* terminal count interrupt mask */
126 | (1<<14) /* interrupt error mask */
127 | (flow_controller<<11) /* flow controller is peripheral or SDMAC */
128 /* we set the same peripheral as source and destination because we
129 * always use memory-to-peripheral or peripheral-to-memory transfers */
130 | (peri<<6) /* dst peripheral */
131 | (peri<<1) /* src peripheral */
132 | (1<<0); /* enable channel */
135 /* isr */
136 void INT_DMAC(void)
138 unsigned int channel;
140 /* SD channel is serviced first */
141 for(channel = 0; channel < 2; channel++)
142 if(DMAC_INT_STATUS & (1<<channel))
144 if(DMAC_INT_ERROR_STATUS & (1<<channel))
145 panicf("DMA error, channel %d", channel);
147 /* clear terminal count interrupt */
148 DMAC_INT_TC_CLEAR = (1<<channel);
150 if(dma_callback[channel])
151 dma_callback[channel]();