mirror of
https://git.planet-casio.com/Lephenixnoir/gint.git
synced 2024-12-29 13:03:36 +01:00
dma: add an asynchronous API
This change adds asynchronous capabilities to the DMA API. Previously, transfers would start asynchronously but could only be completed by a call to dma_transfer_wait(). The API now supports a callback, as well as the dma_transfer_sync() variant, to be consistent with the upcoming USB API that has both _sync and _async versions of functions. The interrupt handler of the DMA was changed to include a return to userland, which is required to perform the callback. * dma_transfer() is now an obsolete synonym for dma_transfer_async() with no callback. * dma_transfer_noint() is now a synonym for dma_transfer_atomic(), for consistency with the upcoming USB API.
This commit is contained in:
parent
42081c9968
commit
acc35d774f
6 changed files with 103 additions and 82 deletions
|
@ -6,6 +6,7 @@
|
|||
#define GINT_DMA
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/defs/call.h>
|
||||
|
||||
/* dma_size_t - Transfer block size */
|
||||
typedef enum
|
||||
|
@ -38,11 +39,12 @@ typedef enum
|
|||
|
||||
} dma_address_t;
|
||||
|
||||
/* dma_transfer() - Start a data transfer on channel 0
|
||||
This function returns just when the transfer starts. The transfer will end
|
||||
later on and the DMA will be stopped by an interrupt. Call
|
||||
dma_transfer_wait() if you need to wait for the transfer to finish. Don't
|
||||
start a new transfer until the current one is finished!
|
||||
/* dma_transfer_async(): Perform an asynchronous DMA data transfer
|
||||
|
||||
This function starts a DMA data transfer and returns immediately. The
|
||||
provided callback will be invoked once the transfer is finish. You can also
|
||||
call dma_transfer_wait() to wait until the transfer completes. You can
|
||||
create a callback with GINT_CALL() or pass GINT_CALL_NULL.
|
||||
|
||||
@channel DMA channel (0..5)
|
||||
@size Transfer size
|
||||
|
@ -50,32 +52,40 @@ typedef enum
|
|||
@src Source pointer, must be aligned with transfer size
|
||||
@src_mode Source address mode
|
||||
@dst Destination address, must be aligned with transfer size
|
||||
@dst_mode Destination address mode */
|
||||
void dma_transfer(int channel, dma_size_t size, uint length,
|
||||
void const *src, dma_address_t src_mode,
|
||||
void *dst, dma_address_t dst_mode);
|
||||
|
||||
/* dma_transfer_wait() - Wait for a transfer to finish
|
||||
|
||||
You should call this function when you need to transfer to be complete
|
||||
before continuing execution. If you are sure that the transfer is finished,
|
||||
this is not necessary (the only way to know is to look at the DMA registers
|
||||
or record interrupts).
|
||||
@dst_mode Destination address mode
|
||||
@callback Function to invoke when the transfer finishes
|
||||
-> Returns true on success. */
|
||||
bool dma_transfer_async(int channel, dma_size_t size, uint length,
|
||||
void const *src, dma_address_t src_mode, void *dst,
|
||||
dma_address_t dst_mode, gint_call_t callback);
|
||||
|
||||
/* dma_transfer_wait(): Wait for an asynchronous transfer to finish
|
||||
@channel DMA channel (0..5) */
|
||||
void dma_transfer_wait(int channel);
|
||||
|
||||
/* dma_transfer_noint() - Perform a data transfer without interrupts
|
||||
This function performs a transfer much like dma_transfer(), but doesn't use
|
||||
interrupts and *actively waits* for the transfer to finish, returning when
|
||||
it's finished. Don't call dma_transfer_wait() after using this function.
|
||||
/* dma_transfer_sync(): Perform an synchronous DMA data transfer
|
||||
Like dma_transfer_async(), but only returns once the transfer completes. */
|
||||
bool dma_transfer_sync(int channel, dma_size_t size, uint length,
|
||||
void const *src, dma_address_t src_mode, void *dst,
|
||||
dma_address_t dst_mode);
|
||||
|
||||
Not using interrupts is a bad design idea for a majority of programs, and is
|
||||
only ever needed to display panic messages inside exception handlers. */
|
||||
void dma_transfer_noint(int channel, dma_size_t size, uint blocks,
|
||||
/* dma_transfer_atomic(): Perform a data transfer without interrupts
|
||||
|
||||
This function performs a transfer much like dma_transfer_sync(), but doesn't
|
||||
use interrupts and actively waits for the transfer to finish. Not using
|
||||
interrupts is a bad design idea for a majority of programs, and is only ever
|
||||
needed to display panic messages inside exception handlers. */
|
||||
void dma_transfer_atomic(int channel, dma_size_t size, uint blocks,
|
||||
void const *src, dma_address_t src_mode,
|
||||
void *dst, dma_address_t dst_mode);
|
||||
|
||||
/* Deprecated version of dma_transfer_async() that did not have a callback */
|
||||
__attribute__((deprecated("Use dma_transfer_async() instead")))
|
||||
void dma_transfer(int channel, dma_size_t size, uint length, void const *src,
|
||||
dma_address_t src_mode, void *dst, dma_address_t dst_mode);
|
||||
/* Old name for dma_transfer_atomic() */
|
||||
#define dma_transfer_noint dma_transfer_atomic
|
||||
|
||||
//---
|
||||
// DMA-based memory manipulation functions
|
||||
//---
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
|
||||
typedef volatile sh7305_dma_channel_t channel_t;
|
||||
|
||||
/* Callbacks for all channels */
|
||||
static gint_call_t dma_callbacks[6] = { 0 };
|
||||
|
||||
/* dma_channel(): Get address of a DMA channel */
|
||||
static channel_t *dma_channel(int channel)
|
||||
{
|
||||
|
@ -60,8 +63,8 @@ static uint32_t dma_translate(void const *address)
|
|||
//---
|
||||
|
||||
/* dma_setup(): Setup the DMA in interrupt or no-interrupt mode.
|
||||
The first parameters are as for dma_transfer() and dma_transfer_noint(). The
|
||||
last parameter indicates whether interrupts should be used.
|
||||
The first parameters are as for dma_transfer() and dma_transfer_atomic().
|
||||
The last parameter indicates whether interrupts should be used.
|
||||
Returns non-zero if the DMA is busy or a configuration error occurs. */
|
||||
static int dma_setup(int channel, dma_size_t size, uint blocks,
|
||||
void const *src, dma_address_t src_mode,
|
||||
|
@ -103,17 +106,36 @@ static int dma_setup(int channel, dma_size_t size, uint blocks,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* dma_transfer(): Perform a data transfer */
|
||||
void dma_transfer(int channel, dma_size_t size, uint blocks,
|
||||
void const *src, dma_address_t src_mode,
|
||||
void *dst, dma_address_t dst_mode)
|
||||
bool dma_transfer_async(int channel, dma_size_t size, uint blocks,
|
||||
void const *src, dma_address_t src_mode, void *dst,
|
||||
dma_address_t dst_mode, gint_call_t callback)
|
||||
{
|
||||
if(dma_setup(channel, size, blocks, src, src_mode, dst, dst_mode, 1))
|
||||
return;
|
||||
return false;
|
||||
|
||||
dma_callbacks[channel] = callback;
|
||||
|
||||
/* Enable channel, starting the DMA transfer. */
|
||||
channel_t *ch = dma_channel(channel);
|
||||
ch->CHCR.DE = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Interrupt handler for all finished DMA transfers */
|
||||
static void dma_interrupt_transfer_ended(int channel)
|
||||
{
|
||||
channel_t *ch = dma_channel(channel);
|
||||
ch->CHCR.DE = 0;
|
||||
ch->CHCR.TE = 0;
|
||||
|
||||
DMA.OR.AE = 0;
|
||||
DMA.OR.NMIF = 0;
|
||||
|
||||
if(dma_callbacks[channel].function)
|
||||
{
|
||||
gint_call(dma_callbacks[channel]);
|
||||
dma_callbacks[channel] = GINT_CALL_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* dma_transfer_wait(): Wait for a transfer to finish */
|
||||
|
@ -144,8 +166,18 @@ void dma_transfer_wait(int channel)
|
|||
}
|
||||
}
|
||||
|
||||
/* dma_transfer_noint(): Perform a data transfer without interruptions */
|
||||
void dma_transfer_noint(int channel, dma_size_t size, uint blocks,
|
||||
bool dma_transfer_sync(int channel, dma_size_t size, uint length,
|
||||
void const *src, dma_address_t src_mode, void *dst,
|
||||
dma_address_t dst_mode)
|
||||
{
|
||||
if(!dma_transfer_async(channel, size, length, src, src_mode, dst,
|
||||
dst_mode, GINT_CALL_NULL)) return false;
|
||||
dma_transfer_wait(channel);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* dma_transfer_atomic(): Perform a data transfer without interruptions */
|
||||
void dma_transfer_atomic(int channel, dma_size_t size, uint blocks,
|
||||
void const *src, dma_address_t src_mode,
|
||||
void *dst, dma_address_t dst_mode)
|
||||
{
|
||||
|
@ -169,6 +201,14 @@ void dma_transfer_noint(int channel, dma_size_t size, uint blocks,
|
|||
DMA.OR.NMIF = 0;
|
||||
}
|
||||
|
||||
/* Deprecated version of dma_transfer_async() that did not have a callback */
|
||||
void dma_transfer(int channel, dma_size_t size, uint length, void const *src,
|
||||
dma_address_t src_mode, void *dst, dma_address_t dst_mode)
|
||||
{
|
||||
dma_transfer_async(channel, size, length, src, src_mode, dst, dst_mode,
|
||||
GINT_CALL_NULL);
|
||||
}
|
||||
|
||||
//---
|
||||
// Initialization
|
||||
//---
|
||||
|
@ -183,14 +223,11 @@ static void configure(void)
|
|||
|
||||
for(int i = 0; i < 6; i++)
|
||||
{
|
||||
/* Install interrupt handler */
|
||||
void *h = intc_handler(codes[i], inth_dma_te, 32);
|
||||
channel_t *ch = dma_channel(i);
|
||||
intc_handler_function(codes[i],
|
||||
GINT_CALL(dma_interrupt_transfer_ended, i));
|
||||
|
||||
/* Set its CHCR address */
|
||||
*(volatile uint32_t **)(h + 24) = &ch->CHCR.lword;
|
||||
/* Clear the enable flag */
|
||||
ch->CHCR.DE = 0;
|
||||
/* Disable the channel */
|
||||
dma_channel(i)->CHCR.DE = 0;
|
||||
}
|
||||
|
||||
/* Install the address error gate */
|
||||
|
|
|
@ -1,37 +1,14 @@
|
|||
/*
|
||||
** gint:dma:inth - Interrupt handler for the DMA
|
||||
** An easy one, just clears some flags and marks all transfers as finished.
|
||||
** gint:dma:inth - DMA address error handler
|
||||
** A particular handler that jumps into a panic.
|
||||
*/
|
||||
|
||||
.global _inth_dma_te
|
||||
.global _inth_dma_ae
|
||||
.global _inth_dma_ae /* 32 bytes */
|
||||
|
||||
.section .gint.blocks, "ax"
|
||||
.align 4
|
||||
|
||||
/* DMA TRANSFER ENDED INTERRUPT HANDLER - 32 BYTES */
|
||||
|
||||
_inth_dma_te:
|
||||
/* Clear the TE flag and DMA Enable in CHCR */
|
||||
mov.l 1f, r1
|
||||
mov.l @r1, r0
|
||||
mov #-4, r2
|
||||
and r2, r0
|
||||
mov.l r0, @r1
|
||||
|
||||
/* Clear the AE and NMIF flags in OR */
|
||||
mov.l 2f, r1
|
||||
mov.w @r1, r0
|
||||
mov #-7, r2
|
||||
and r2, r0
|
||||
mov.w r0, @r1
|
||||
|
||||
rts
|
||||
nop
|
||||
|
||||
1: .long 0 /* CHCR, set dynamically */
|
||||
2: .long 0xfe008060 /* DMA.OR */
|
||||
|
||||
/* DMA ADDRESS ERROR INTERRUPT HANDLER - 18 BYTES */
|
||||
/* DMA ADDRESS ERROR INTERRUPT HANDLER - 22 BYTES */
|
||||
|
||||
_inth_dma_ae:
|
||||
/* Manually RTE into the panic routine, preserving SPC */
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
void *dma_memcpy(void * __restrict dst, const void * __restrict src,
|
||||
size_t size)
|
||||
{
|
||||
dma_transfer(1, DMA_32B, size >> 5, src, DMA_INC, dst, DMA_INC);
|
||||
dma_transfer_wait(1);
|
||||
|
||||
dma_transfer_sync(1, DMA_32B, size >> 5, src, DMA_INC, dst, DMA_INC);
|
||||
return dst;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ void *dma_memset(void *dst, uint32_t l, size_t size)
|
|||
different memory regions, making the DMA faster than the CPU. */
|
||||
for(int i = 0; i < 8; i++) ILbuf[i] = l;
|
||||
|
||||
dma_transfer(1, DMA_32B, size >> 5, ILbuf, DMA_FIXED, dst, DMA_INC);
|
||||
dma_transfer_wait(1);
|
||||
dma_transfer_sync(1, DMA_32B, size>>5, ILbuf, DMA_FIXED, dst, DMA_INC);
|
||||
return dst;
|
||||
}
|
||||
|
|
|
@ -150,18 +150,18 @@ void r61524_display(uint16_t *vram, int start, int height, int method)
|
|||
appear. */
|
||||
int blocks = 99 * (height >> 2);
|
||||
|
||||
/* Now roll! */
|
||||
if(method == R61524_DMA)
|
||||
{
|
||||
/* If the previous transfer is still running, wait for it */
|
||||
if(method == R61524_DMA) {
|
||||
/* If the previous transfer is still running, wait for it; then
|
||||
start sending asynchronously and return. */
|
||||
dma_transfer_wait(0);
|
||||
/* Usa a normal, interrupt-based transfer */
|
||||
dma_transfer(0, DMA_32B, blocks, src, DMA_INC, dst, DMA_FIXED);
|
||||
|
||||
/* Function returns early so that rendering can continue on
|
||||
another VRAM while the transfer is still being done */
|
||||
dma_transfer_async(0, DMA_32B, blocks, src, DMA_INC, dst,
|
||||
DMA_FIXED, GINT_CALL_NULL);
|
||||
}
|
||||
else {
|
||||
/* Transfer atomically */
|
||||
dma_transfer_atomic(0, DMA_32B, blocks, src, DMA_INC, dst,
|
||||
DMA_FIXED);
|
||||
}
|
||||
else dma_transfer_noint(0, DMA_32B, blocks, src,DMA_INC,dst,DMA_FIXED);
|
||||
}
|
||||
|
||||
//---
|
||||
|
|
Loading…
Reference in a new issue