summaryrefslogtreecommitdiff
path: root/firmware/target/mips/ingenic_x1000/pcm-x1000.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/mips/ingenic_x1000/pcm-x1000.c')
-rw-r--r--firmware/target/mips/ingenic_x1000/pcm-x1000.c165
1 files changed, 165 insertions, 0 deletions
diff --git a/firmware/target/mips/ingenic_x1000/pcm-x1000.c b/firmware/target/mips/ingenic_x1000/pcm-x1000.c
new file mode 100644
index 0000000000..9ae6f5a956
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/pcm-x1000.c
@@ -0,0 +1,165 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
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 "audiohw.h"
25#include "pcm.h"
26#include "pcm-internal.h"
27#include "panic.h"
28#include "dma-x1000.h"
29#include "irq-x1000.h"
30#include "x1000/aic.h"
31
32#define AIC_STATE_STOPPED 0
33#define AIC_STATE_PLAYING 1
34
35volatile unsigned aic_tx_underruns = 0;
36
37static int aic_state = AIC_STATE_STOPPED;
38
39static int aic_lock = 0;
40static volatile int aic_dma_pending_event = DMA_EVENT_NONE;
41
42static dma_desc aic_dma_desc;
43
44static void pcm_dma_int_cb(int event);
45
46void pcm_play_dma_init(void)
47{
48 /* Let the target initialize its hardware and setup the AIC */
49 audiohw_init();
50
51 /* Set DMA callback */
52 dma_set_callback(DMA_CHANNEL_AUDIO, pcm_dma_int_cb);
53
54 /* Program FIFO threshold -- DMA settings must match */
55 jz_writef(AIC_CFG, TFTH(16));
56
57 /* Ensure all playback is disabled */
58 jz_writef(AIC_CCR, ERPL(0));
59
60 /* Enable the controller */
61 jz_writef(AIC_CFG, ENABLE(1));
62
63 /* Enable interrupts */
64 system_enable_irq(IRQ_AIC);
65}
66
67void pcm_play_dma_postinit(void)
68{
69 audiohw_postinit();
70}
71
72void pcm_dma_apply_settings(void)
73{
74 audiohw_set_frequency(pcm_fsel);
75}
76
77static void pcm_dma_start(const void* addr, size_t size)
78{
79 aic_dma_desc.cm = jz_orf(DMA_CHN_CM, SAI(1), DAI(0), RDIL(9),
80 SP_V(32BIT), DP_V(32BIT), TSZ_V(AUTO),
81 STDE(0), TIE(1), LINK(0));
82 aic_dma_desc.sa = PHYSADDR(addr);
83 aic_dma_desc.ta = PHYSADDR(JA_AIC_DR);
84 aic_dma_desc.tc = size;
85 aic_dma_desc.sd = 0;
86 aic_dma_desc.rt = jz_orf(DMA_CHN_RT, TYPE_V(I2S_TX));
87 aic_dma_desc.pad0 = 0;
88 aic_dma_desc.pad1 = 0;
89
90 commit_dcache_range(&aic_dma_desc, sizeof(dma_desc));
91 commit_dcache_range(addr, size);
92
93 REG_DMA_CHN_DA(DMA_CHANNEL_AUDIO) = PHYSADDR(&aic_dma_desc);
94 jz_writef(DMA_CHN_CS(DMA_CHANNEL_AUDIO), DES8(1), NDES(0));
95 jz_set(DMA_DB, 1 << DMA_CHANNEL_AUDIO);
96 jz_writef(DMA_CHN_CS(DMA_CHANNEL_AUDIO), CTE(1));
97
98 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
99}
100
101static void pcm_dma_handle_event(int event)
102{
103 if(event == DMA_EVENT_COMPLETE) {
104 const void* addr;
105 size_t size;
106 if(pcm_play_dma_complete_callback(PCM_DMAST_OK, &addr, &size))
107 pcm_dma_start(addr, size);
108 } else if(event == DMA_EVENT_NONE) {
109 /* ignored, so callers don't need to check for this */
110 } else {
111 pcm_play_dma_status_callback(PCM_DMAST_ERR_DMA);
112 }
113}
114
115static void pcm_dma_int_cb(int event)
116{
117 if(aic_lock) {
118 aic_dma_pending_event = event;
119 return;
120 } else {
121 pcm_dma_handle_event(event);
122 }
123}
124
125void pcm_play_dma_start(const void* addr, size_t size)
126{
127 aic_dma_pending_event = DMA_EVENT_NONE;
128 aic_state = AIC_STATE_PLAYING;
129
130 pcm_dma_start(addr, size);
131 jz_writef(AIC_CCR, TDMS(1), ETUR(1), ERPL(1));
132}
133
134void pcm_play_dma_stop(void)
135{
136 jz_writef(AIC_CCR, TDMS(0), ETUR(0), ERPL(0));
137 jz_writef(AIC_CCR, TFLUSH(1));
138
139 aic_dma_pending_event = DMA_EVENT_NONE;
140 aic_state = AIC_STATE_STOPPED;
141}
142
143void pcm_play_lock(void)
144{
145 ++aic_lock;
146}
147
148void pcm_play_unlock(void)
149{
150 int irq = disable_irq_save();
151 if(--aic_lock == 0 && aic_state == AIC_STATE_PLAYING) {
152 pcm_dma_handle_event(aic_dma_pending_event);
153 aic_dma_pending_event = DMA_EVENT_NONE;
154 }
155
156 restore_irq(irq);
157}
158
159void AIC(void)
160{
161 if(jz_readf(AIC_SR, TUR)) {
162 aic_tx_underruns += 1;
163 jz_writef(AIC_SR, TUR(0));
164 }
165}