summaryrefslogtreecommitdiff
path: root/firmware/target/mips/ingenic_jz47xx/pcm-jz4760.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/mips/ingenic_jz47xx/pcm-jz4760.c')
-rw-r--r--firmware/target/mips/ingenic_jz47xx/pcm-jz4760.c240
1 files changed, 240 insertions, 0 deletions
diff --git a/firmware/target/mips/ingenic_jz47xx/pcm-jz4760.c b/firmware/target/mips/ingenic_jz47xx/pcm-jz4760.c
new file mode 100644
index 0000000000..39df037f76
--- /dev/null
+++ b/firmware/target/mips/ingenic_jz47xx/pcm-jz4760.c
@@ -0,0 +1,240 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2016 by Roman Stolyarov
11 *
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.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "system.h"
23#include "kernel.h"
24#include "logf.h"
25#include "audio.h"
26#include "sound.h"
27#include "pcm.h"
28#include "pcm-internal.h"
29#include "cpu.h"
30
31
32/****************************************************************************
33 ** Playback DMA transfer
34 **/
35
36void pcm_play_dma_postinit(void)
37{
38 audiohw_postinit();
39
40 /* Flush FIFO */
41 __aic_flush_tfifo();
42}
43
44void pcm_play_dma_init(void)
45{
46 system_enable_irq(DMA_IRQ(DMA_AIC_TX_CHANNEL));
47
48 /* Initialize default register values. */
49 audiohw_init();
50}
51
52void pcm_dma_apply_settings(void)
53{
54 audiohw_set_frequency(pcm_fsel);
55}
56
57static const void* playback_address;
58static inline void set_dma(const void *addr, size_t size)
59{
60 int burst_size;
61 logf("%x %d %x", (unsigned int)addr, size, REG_AIC_SR);
62
63 dma_cache_wback_inv((unsigned long)addr, size);
64
65 if(size % 16)
66 {
67 if(size % 4)
68 {
69 size /= 2;
70 burst_size = DMAC_DCMD_DS_16BIT;
71 }
72 else
73 {
74 size /= 4;
75 burst_size = DMAC_DCMD_DS_32BIT;
76 }
77 }
78 else
79 {
80 size /= 16;
81 burst_size = DMAC_DCMD_DS_16BYTE;
82 }
83
84 REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) = 0;
85 REG_DMAC_DSAR(DMA_AIC_TX_CHANNEL) = PHYSADDR((unsigned long)addr);
86 REG_DMAC_DTAR(DMA_AIC_TX_CHANNEL) = PHYSADDR((unsigned long)AIC_DR);
87 REG_DMAC_DTCR(DMA_AIC_TX_CHANNEL) = size;
88 REG_DMAC_DRSR(DMA_AIC_TX_CHANNEL) = DMAC_DRSR_RS_AICOUT;
89 REG_DMAC_DCMD(DMA_AIC_TX_CHANNEL) = (DMAC_DCMD_SAI | DMAC_DCMD_SWDH_32 | burst_size | DMAC_DCMD_DWDH_16 | DMAC_DCMD_TIE);
90 REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) = DMAC_DCCSR_NDES | DMAC_DCCSR_EN;
91
92 playback_address = addr;
93}
94
95static inline void play_dma_callback(void)
96{
97 const void *start;
98 size_t size;
99
100 if (pcm_play_dma_complete_callback(PCM_DMAST_OK, &start, &size))
101 {
102 set_dma(start, size);
103 REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) |= DMAC_DCCSR_EN;
104 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
105 }
106}
107
108void DMA_CALLBACK(DMA_AIC_TX_CHANNEL)(void) __attribute__ ((section(".icode")));
109void DMA_CALLBACK(DMA_AIC_TX_CHANNEL)(void)
110{
111 if (REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) & DMAC_DCCSR_AR)
112 {
113 logf("PCM DMA address error");
114 REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) &= ~DMAC_DCCSR_AR;
115 }
116
117 if (REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) & DMAC_DCCSR_HLT)
118 {
119 logf("PCM DMA halt");
120 REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) &= ~DMAC_DCCSR_HLT;
121 }
122
123 if (REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) & DMAC_DCCSR_TT)
124 {
125 REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) &= ~DMAC_DCCSR_TT;
126 play_dma_callback();
127 }
128}
129
130void pcm_play_dma_start(const void *addr, size_t size)
131{
132 __dmac_channel_enable_clk(DMA_AIC_TX_CHANNEL);
133
134 set_dma(addr, size);
135
136 __aic_enable_replay();
137
138 __dmac_channel_enable_irq(DMA_AIC_TX_CHANNEL);
139}
140
141void pcm_play_dma_stop(void)
142{
143 int flags = disable_irq_save();
144
145 REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) = (REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) | DMAC_DCCSR_HLT) & ~DMAC_DCCSR_EN;
146
147 __dmac_channel_disable_clk(DMA_AIC_TX_CHANNEL);
148
149 __aic_disable_replay();
150
151 restore_irq(flags);
152}
153
154static unsigned int play_lock = 0;
155void pcm_play_lock(void)
156{
157 int flags = disable_irq_save();
158
159 if (++play_lock == 1)
160 __dmac_channel_disable_irq(DMA_AIC_TX_CHANNEL);
161
162 restore_irq(flags);
163}
164
165void pcm_play_unlock(void)
166{
167 int flags = disable_irq_save();
168
169 if (--play_lock == 0)
170 __dmac_channel_enable_irq(DMA_AIC_TX_CHANNEL);
171
172 restore_irq(flags);
173}
174
175void pcm_play_dma_pause(bool pause)
176{
177 int flags = disable_irq_save();
178
179 if(pause)
180 REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) &= ~DMAC_DCCSR_EN;
181 else
182 REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) |= DMAC_DCCSR_EN;
183
184 restore_irq(flags);
185}
186
187static int get_dma_count(void)
188{
189 int count = REG_DMAC_DTCR(DMA_AIC_TX_CHANNEL);
190 switch(REG_DMAC_DCMD(DMA_AIC_TX_CHANNEL) & DMAC_DCMD_DS_MASK)
191 {
192 case DMAC_DCMD_DS_16BIT:
193 count *= 2;
194 break;
195 case DMAC_DCMD_DS_32BIT:
196 count *= 4;
197 break;
198 case DMAC_DCMD_DS_16BYTE:
199 count *= 16;
200 break;
201 }
202
203 return count;
204}
205
206size_t pcm_get_bytes_waiting(void)
207{
208 int bytes, flags = disable_irq_save();
209
210 if(REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) & DMAC_DCCSR_EN)
211 bytes = get_dma_count() & ~3;
212 else
213 bytes = 0;
214
215 restore_irq(flags);
216
217 return bytes;
218}
219
220const void * pcm_play_dma_get_peak_buffer(int *count)
221{
222 int flags = disable_irq_save();
223
224 const void* addr;
225 if(REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) & DMAC_DCCSR_EN)
226 {
227 int bytes = get_dma_count();
228 *count = bytes >> 2;
229 addr = (const void*)((int)(playback_address + bytes + 2) & ~3);
230 }
231 else
232 {
233 *count = 0;
234 addr = NULL;
235 }
236
237 restore_irq(flags);
238
239 return addr;
240}