summaryrefslogtreecommitdiff
path: root/firmware/export
diff options
context:
space:
mode:
authorCástor Muñoz <cmvidal@gmail.com>2014-12-06 18:33:11 +0100
committerCástor Muñoz <cmvidal@gmail.com>2015-10-07 06:15:03 +0200
commitd6ee2c9eafbf5381695d1c7eb01801855c85222b (patch)
treea6ae8fb8deeebfece6f110a0e44643fa8194662e /firmware/export
parent609cde94689b20098be1c36cc5157514b6dd63a4 (diff)
downloadrockbox-d6ee2c9eafbf5381695d1c7eb01801855c85222b.tar.gz
rockbox-d6ee2c9eafbf5381695d1c7eb01801855c85222b.zip
iPod Classic: introduce PL080 DMA controller driver
Motivation: This driver began as a set of functions to help to test and experiment with different DMA configurations. It is cumbersome, time consuming, and leads to mistakes to handle LLIs and DMA registers dispersed along the code. Later, i decided to adapt an old DMA queue driver written in the past for a similar (scatter-gather) controller, all task/queue code is based on the old driver. Finally, some cleaning and dmac_ch_get_info() function was added to complete RB needs. Description: - Generic, can be used by other targets including the same controller. Not difficult to adapt for other similar controllers if necesary. - Easy to experiment and compare results using different setups and/or queue algorithms: Multi-controller and fully configurable from an unique place. All task and LLI management is done by the driver, user only has to (statically) allocate them. - Two queue modes: QUEUE_NORMAL: each task in the queue is launched using a new DMA transfer once previous task is finished. QUEUE_LINK: when a task is queued, it is linked with the last queued task, creating a single continuous DMA transfer. New tasks must be queued while the channel is running, otherwise the continuous DMA transfer will be broken. On Classic, QUEUE_LINK mode is needed for I2S continuous transfers, QUEUE_NORMAL is used for LCD and could be useful in the future for I2C or UART (non-blocking serial debug) if necessary. - Robust DMA transfer progress info (peak meter), needs final testing, see below. Technical details about DMA progress: There are comments in the code related to the method actually used (sequence method), it reads progress without halting the DMA transfer. Althought the datasheet does not recommend to do that, the sequence method seems to be robust, I ran tests calling dmac_ch_get_info() millions of times and the results were always as expected (tests done at 2:1 CPU/AHB clock ratio, no other ratios were tried but probably sequence method will work for any typical ratio). This controller allows to halt the transfer and drain the DMAC FIFO, DMA requests are ignored when the DMA channel is halted. This method is not suitable for playback because FIFO is never drained to I2S peripheral (who raises the DMA requests). This method probably works for capture, the FIFO is drained to memory before halting. Another way is to disable (stop) the playback channel. When the channel is disabled, all FIFO data is lost. It is unknown how much the FIFO was filled when it was cleared, SRCADDR counter includes the lost data, therefore the only useful information is LINK and COUNT, that is the same information disponible when using the sequence method. At this point we must procced in the same way as in sequence method, in addition the playback channel should be relaunched (configure + start) after calculating real SRCADDR. The stop+relaunch method should work, it is a bit complicated, and not valid for all peripheral FIFO configurations (depending on stream rate). Moreover, due to the way the COUNT register is implemented in HW, I suspect that this method will fail when source and destination bus widths doesn't match. And more important, it is not easy to garantize that no sample is lost here or there, using the sequence method we can always be sure that playback is ok. Change-Id: Ib12a1e2992e2b6da4fc68431128c793a21b4b540
Diffstat (limited to 'firmware/export')
-rw-r--r--firmware/export/pl080.h269
-rw-r--r--firmware/export/s5l8702.h4
2 files changed, 273 insertions, 0 deletions
diff --git a/firmware/export/pl080.h b/firmware/export/pl080.h
new file mode 100644
index 0000000000..9e48f07f0c
--- /dev/null
+++ b/firmware/export/pl080.h
@@ -0,0 +1,269 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2014 Cástor Muñoz
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#ifndef _PL080_H
22#define _PL080_H
23
24/*
25 * ARM PrimeCell PL080 Multiple Master DMA controller
26 */
27#include <stddef.h>
28#include <stdbool.h>
29
30/* general defines */
31#define DMAC_CH_COUNT 8
32#define DMAC_LLI_MAX_COUNT 0xfff
33#define DMAC_CH_PRIO(x) (x)
34#define DMAC_CH_BASE(dmac_ba,ch_n) ((dmac_ba) + 0x100 + ((ch_n) << 5))
35
36/* PL080 controller registers */
37#define DMACINTSTS(base) (*((uint32_t volatile*)((base) + 0x00)))
38#define DMACINTTCSTS(base) (*((uint32_t volatile*)((base) + 0x04)))
39#define DMACINTTCCLR(base) (*((uint32_t volatile*)((base) + 0x08)))
40#define DMACINTERRSTS(base) (*((uint32_t volatile*)((base) + 0x0c)))
41#define DMACINTERRCLR(base) (*((uint32_t volatile*)((base) + 0x10)))
42#define DMACRAWINTTCSTS(base) (*((uint32_t volatile*)((base) + 0x14)))
43#define DMACRAWINTERRSTS(base) (*((uint32_t volatile*)((base) + 0x18)))
44#define DMACENABLEDCHANS(base) (*((uint32_t volatile*)((base) + 0x1c)))
45#define DMACSOFTBREQ(base) (*((uint32_t volatile*)((base) + 0x20)))
46#define DMACSOFTSREQ(base) (*((uint32_t volatile*)((base) + 0x24)))
47#define DMACSOFTLBREQ(base) (*((uint32_t volatile*)((base) + 0x28)))
48#define DMACSOFTLSREQ(base) (*((uint32_t volatile*)((base) + 0x2c)))
49#define DMACCONFIG(base) (*((uint32_t volatile*)((base) + 0x30)))
50#define DMACSYNC(base) (*((uint32_t volatile*)((base) + 0x34)))
51
52/* PL080 controller channel registers */
53#define DMACCxSRCADDR(base) (*((void* volatile*)((base) + 0x00)))
54#define DMACCxDESTADDR(base) (*((void* volatile*)((base) + 0x04)))
55#define DMACCxLINK(base) (*((uint32_t volatile*)((base) + 0x08)))
56#define DMACCxCONTROL(base) (*((uint32_t volatile*)((base) + 0x0c)))
57#define DMACCxCONFIG(base) (*((uint32_t volatile*)((base) + 0x10)))
58
59/* PL080 controller channel LLI */
60#define DMACCxLLI(base) ((struct dmac_lli volatile*)(base))
61
62/* PL080 DMA controller configuration register */
63#define DMACCONFIG_E_POS 0 /* DMAC enable */
64#define DMACCONFIG_E_MSK 0x1
65#define DMACCONFIG_M1_POS 1 /* AHB Master 1 endianness */
66#define DMACCONFIG_M1_MSK 0x1
67#define DMACCONFIG_M2_POS 2 /* AHB Master 2 endianness */
68#define DMACCONFIG_M2_MSK 0x1
69
70#define DMACCONFIG_E_BIT (1 << DMACCONFIG_E_POS)
71#define DMACCONFIG_M1_BIT (1 << DMACCCONFI_M1_POS)
72#define DMACCONFIG_M2_BIT (1 << DMACCCONFI_M2_POS)
73
74#define DMACCONFIG_M_LITTLE_ENDIAN 0
75#define DMACCONFIG_M_BIG_ENDIAN 1
76
77/* PL080 DMA controller channel LLI register */
78#define DMACCxLINK_LM_POS 0
79#define DMACCxLINK_LM_MSK 0x1
80#define DMACCxLINK_NEXTLLI_POS 2
81#define DMACCxLINK_NEXTLLI_MSK 0x3fffffff
82
83/* PL080 channel control register */
84#define DMACCxCONTROL_I_POS 31 /* terminal count interrupt */
85#define DMACCxCONTROL_I_MSK 0x1
86#define DMACCxCONTROL_PROT_POS 28 /* protection bits */
87#define DMACCxCONTROL_PROT_MSK 0x7
88#define DMACCxCONTROL_DI_POS 27 /* destination addr increment */
89#define DMACCxCONTROL_DI_MSK 0x1
90#define DMACCxCONTROL_SI_POS 26 /* source addr increment */
91#define DMACCxCONTROL_SI_MSK 0x1
92#define DMACCxCONTROL_D_POS 25 /* destinantion AHB master */
93#define DMACCxCONTROL_D_MSK 0x1
94#define DMACCxCONTROL_S_POS 24 /* source AHB master */
95#define DMACCxCONTROL_S_MSK 0x1
96#define DMACCxCONTROL_DWIDTH_POS 21 /* destinantion transfer width */
97#define DMACCxCONTROL_DWIDTH_MSK 0x7
98#define DMACCxCONTROL_SWIDTH_POS 18 /* source transfer width */
99#define DMACCxCONTROL_SWIDTH_MSK 0x7
100#define DMACCxCONTROL_DBSIZE_POS 15 /* destinantion burst size */
101#define DMACCxCONTROL_DBSIZE_MSK 0x7
102#define DMACCxCONTROL_SBSIZE_POS 12 /* source burst size */
103#define DMACCxCONTROL_SBSIZE_MSK 0x7
104#define DMACCxCONTROL_COUNT_POS 0 /* n SWIDTH size transfers */
105#define DMACCxCONTROL_COUNT_MSK 0xfff
106
107#define DMACCxCONTROL_WIDTH_8 0
108#define DMACCxCONTROL_WIDTH_16 1
109#define DMACCxCONTROL_WIDTH_32 2
110
111#define DMACCxCONTROL_BSIZE_1 0
112#define DMACCxCONTROL_BSIZE_4 1
113#define DMACCxCONTROL_BSIZE_8 2
114#define DMACCxCONTROL_BSIZE_16 3
115#define DMACCxCONTROL_BSIZE_32 4
116#define DMACCxCONTROL_BSIZE_64 5
117#define DMACCxCONTROL_BSIZE_128 6
118#define DMACCxCONTROL_BSIZE_256 7
119
120#define DMACCxCONTROL_INC_DISABLE 0
121#define DMACCxCONTROL_INC_ENABLE 1
122
123#define DMACCxCONTROL_I_BIT (1 << DMACCxCONTROL_I_POS)
124
125/* protection bits */
126#define DMAC_PROT_PRIV (1 << 0)
127#define DMAC_PROT_BUFF (1 << 1)
128#define DMAC_PROT_CACH (1 << 2)
129
130/* bus */
131#define DMAC_MASTER_AHB1 0
132#define DMAC_MASTER_AHB2 1
133
134/* PL080 channel configuration register */
135#define DMACCxCONFIG_E_POS 0 /* enable */
136#define DMACCxCONFIG_E_MSK 0x1
137#define DMACCxCONFIG_SRCPERI_POS 1 /* source peripheral */
138#define DMACCxCONFIG_SRCPERI_MSK 0xf
139#define DMACCxCONFIG_DESTPERI_POS 6 /* destination peripheral */
140#define DMACCxCONFIG_DESTPERI_MSK 0xf
141#define DMACCxCONFIG_FLOWCNTRL_POS 11 /* DMA transfer type */
142#define DMACCxCONFIG_FLOWCNTRL_MSK 0x7
143#define DMACCxCONFIG_IE_POS 14 /* interrupt error mask */
144#define DMACCxCONFIG_IE_MSK 0x1
145#define DMACCxCONFIG_ITC_POS 15 /* interrupt terminal count mask */
146#define DMACCxCONFIG_ITC_MSK 0x1
147#define DMACCxCONFIG_L_POS 16 /* lock */
148#define DMACCxCONFIG_L_MSK 0x1
149#define DMACCxCONFIG_A_POS 17 /* active */
150#define DMACCxCONFIG_A_MSK 0x1
151#define DMACCxCONFIG_H_POS 18 /* halt */
152#define DMACCxCONFIG_H_MSK 0x1
153
154#define DMACCxCONFIG_E_BIT (1 << DMACCxCONFIG_E_POS)
155#define DMACCxCONFIG_IE_BIT (1 << DMACCxCONFIG_IE_POS)
156#define DMACCxCONFIG_ITC_BIT (1 << DMACCxCONFIG_ITC_POS)
157#define DMACCxCONFIG_L_BIT (1 << DMACCxCONFIG_L_POS)
158#define DMACCxCONFIG_A_BIT (1 << DMACCxCONFIG_A_POS)
159#define DMACCxCONFIG_H_BIT (1 << DMACCxCONFIG_H_POS)
160
161#define DMACCxCONFIG_FLOWCNTRL_MEMMEM_DMA 0
162#define DMACCxCONFIG_FLOWCNTRL_MEMPERI_DMA 1
163#define DMACCxCONFIG_FLOWCNTRL_PERIMEM_DMA 2
164#define DMACCxCONFIG_FLOWCNTRL_PERIPERI_DMA 3
165#define DMACCxCONFIG_FLOWCNTRL_PERIPERI_DSTPERI 4
166#define DMACCxCONFIG_FLOWCNTRL_MEMPERI_PERI 5
167#define DMACCxCONFIG_FLOWCNTRL_PERIMEM_PERI 6
168#define DMACCxCONFIG_FLOWCNTRL_PERIPERI_SRCPERI 7
169
170/*
171 * types
172 */
173struct dmac_lli {
174 void* srcaddr;
175 void* dstaddr;
176 uint32_t link;
177 uint32_t control;
178} __attribute__((aligned(16)));
179
180struct dmac_tsk {
181 struct dmac_lli volatile *start_lli;
182 struct dmac_lli volatile *end_lli;
183 uint32_t size;
184 void *cb_data;
185};
186
187/* used when src/dst peri is memory */
188#define DMAC_PERI_NONE 0x80
189
190struct dmac_ch_cfg {
191 uint8_t srcperi;
192 uint8_t dstperi;
193 uint8_t sbsize;
194 uint8_t dbsize;
195 uint8_t swidth;
196 uint8_t dwidth;
197 uint8_t sbus;
198 uint8_t dbus;
199 uint8_t sinc;
200 uint8_t dinc;
201 uint8_t prot;
202 uint16_t lli_xfer_max_count;
203};
204
205struct dmac_ch {
206 /** user configurable data **/
207 struct dmac *dmac;
208 unsigned int prio;
209 void (*cb_fn)(void *cb_data);
210 /* tsk circular buffer */
211 struct dmac_tsk *tskbuf;
212 uint32_t tskbuf_mask;
213 uint32_t queue_mode;
214 /* lli circular buffer */
215 struct dmac_lli volatile *llibuf;
216 uint32_t llibuf_mask;
217 uint32_t llibuf_bus;
218
219 /** private driver data **/
220 uint32_t baddr;
221 struct dmac_lli volatile *llibuf_top;
222 uint32_t tasks_queued; /* roll-over counter */
223 uint32_t tasks_done; /* roll-over counter */
224 uint32_t control;
225 struct dmac_ch_cfg *cfg;
226};
227
228struct dmac {
229 /* user configurable data */
230 const uint32_t baddr;
231 const uint8_t m1;
232 const uint8_t m2;
233
234 /* driver private data */
235 struct dmac_ch *ch_l[DMAC_CH_COUNT];
236 uint32_t ch_run_status; /* channel running status mask */
237};
238
239/* dmac_ch->queue_mode */
240enum {
241 QUEUE_NORMAL,
242 QUEUE_LINK,
243};
244
245/*
246 * prototypes
247 */
248void dmac_callback(struct dmac *dmac);
249
250void dmac_open(struct dmac *dmac);
251
252void dmac_ch_init(struct dmac_ch *ch, struct dmac_ch_cfg *cfg);
253
254void dmac_ch_lock_int(struct dmac_ch *ch);
255void dmac_ch_unlock_int(struct dmac_ch *ch);
256
257void dmac_ch_queue_2d(struct dmac_ch *ch, void *srcaddr, void *dstaddr,
258 size_t size, size_t width, size_t stride, void *cb_data);
259#define dmac_ch_queue(ch, srcaddr, dstaddr, size, cb_data) \
260 dmac_ch_queue_2d(ch, srcaddr, dstaddr, size, 0, 0, cb_data)
261
262void dmac_ch_stop(struct dmac_ch* ch);
263
264bool dmac_ch_running(struct dmac_ch *ch);
265
266void *dmac_ch_get_info(struct dmac_ch *ch,
267 size_t *bytes, size_t *t_bytes);
268
269#endif /* _PL080_H */
diff --git a/firmware/export/s5l8702.h b/firmware/export/s5l8702.h
index a176a9bf29..701dcabeb4 100644
--- a/firmware/export/s5l8702.h
+++ b/firmware/export/s5l8702.h
@@ -429,6 +429,9 @@ struct dma_lli
429 uint32_t control; 429 uint32_t control;
430}; 430};
431#endif 431#endif
432/* currently these definitions are not used, temporarily
433 removed to avoid conflicts with pl080.h */
434#if 0
432#define DMACINTSTS(d) (*((uint32_t volatile*)(0x38200000 + 0x1700000 * (d)))) 435#define DMACINTSTS(d) (*((uint32_t volatile*)(0x38200000 + 0x1700000 * (d))))
433#define DMACINTTCSTS(d) (*((uint32_t volatile*)(0x38200004 + 0x1700000 * (d)))) 436#define DMACINTTCSTS(d) (*((uint32_t volatile*)(0x38200004 + 0x1700000 * (d))))
434#define DMACINTTCCLR(d) (*((uint32_t volatile*)(0x38200008 + 0x1700000 * (d)))) 437#define DMACINTTCCLR(d) (*((uint32_t volatile*)(0x38200008 + 0x1700000 * (d))))
@@ -443,6 +446,7 @@ struct dma_lli
443#define DMACSOFTLSREQ(d) (*((uint32_t volatile*)(0x3820002c + 0x1700000 * (d)))) 446#define DMACSOFTLSREQ(d) (*((uint32_t volatile*)(0x3820002c + 0x1700000 * (d))))
444#define DMACCONFIG(d) (*((uint32_t volatile*)(0x38200030 + 0x1700000 * (d)))) 447#define DMACCONFIG(d) (*((uint32_t volatile*)(0x38200030 + 0x1700000 * (d))))
445#define DMACSYNC(d) (*((uint32_t volatile*)(0x38200034 + 0x1700000 * (d)))) 448#define DMACSYNC(d) (*((uint32_t volatile*)(0x38200034 + 0x1700000 * (d))))
449#endif
446#define DMACCLLI(d, c) (*((struct dma_lli volatile*)(0x38200100 + 0x1700000 * (d) + 0x20 * (c)))) 450#define DMACCLLI(d, c) (*((struct dma_lli volatile*)(0x38200100 + 0x1700000 * (d) + 0x20 * (c))))
447#define DMACCSRCADDR(d, c) (*((const void* volatile*)(0x38200100 + 0x1700000 * (d) + 0x20 * (c)))) 451#define DMACCSRCADDR(d, c) (*((const void* volatile*)(0x38200100 + 0x1700000 * (d) + 0x20 * (c))))
448#define DMACCDESTADDR(d, c) (*((void* volatile*)(0x38200104 + 0x1700000 * (d) + 0x20 * (c)))) 452#define DMACCDESTADDR(d, c) (*((void* volatile*)(0x38200104 + 0x1700000 * (d) + 0x20 * (c))))