summaryrefslogtreecommitdiff
path: root/arm9/lib/NitroSDK/src/MI_dma_gxcommand.c
blob: 26231999e4751b6c9777f671049069cddf563fa4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#include "MI_dma_gxcommand.h"
#include "function_target.h"
#include "OS_interrupt.h"
#include "OS_reset.h"
#include "sections.h"

static MIiGXDmaParams MIi_GXDmaParams = { FALSE };

static void MIi_FIFOCallback(void);
static void MIi_DMACallback(void *arg);
static void MIi_DMAFastCallback(void *arg);

#pragma section ITCM begin
ARM_FUNC void MI_SendGXCommand(u32 dmaNo, const void *src, u32 commandLength)
{
    vu32 *dmaCntp;
    u32 leftLength = commandLength;
    u32 currentSrc = (u32)src;
    if (!leftLength)
    {
        return;
    }

    MIi_CheckDma0SourceAddress(dmaNo, (u32)src, commandLength, DMA_SRC_INC);

    do
    {
        dmaCntp = &((vu32 *)REG_ADDR_DMA0SAD)[dmaNo * 3 + 2];
        while (*dmaCntp & 0x80000000) {}
    } while(0);

    while (leftLength > 0)
    {
        u32 length = (leftLength > MIi_GX_LENGTH_ONCE) ? MIi_GX_LENGTH_ONCE : leftLength;
        MIi_DmaSetParams(dmaNo, currentSrc, (u32)REG_GXFIFO_ADDR, MI_CNT_SEND32(length));
        leftLength -= length;
        currentSrc += length;
    }

    do
    {
        while (*dmaCntp & 0x80000000) {}
    } while(0);
}
#pragma section ITCM end

ARM_FUNC void MI_SendGXCommandAsync(u32 dmaNo, const void *src, u32 commandLength, MIDmaCallback callback, void *arg)
{
    if (!commandLength)
    {
        MIi_CallCallback(callback, arg);
        return;
    }

    while (MIi_GXDmaParams.isBusy) {}

    while (!(G3X_GetCommandFifoStatus() & GX_FIFOSTAT_UNDERHALF)) {}

    MIi_GXDmaParams.isBusy = TRUE;
    MIi_GXDmaParams.dmaNo = dmaNo;
    MIi_GXDmaParams.src = (u32)src;
    MIi_GXDmaParams.length = commandLength;
    MIi_GXDmaParams.callback = callback;
    MIi_GXDmaParams.arg = arg;

    MIi_CheckDma0SourceAddress(dmaNo, (u32)src, commandLength, DMA_SRC_INC);

    MI_WaitDma(dmaNo);
    {
        OSIntrMode lastIntrMode = OS_DisableInterrupts();

        MIi_GXDmaParams.fifoCond = (GXFifoIntrCond)((reg_G3X_GXSTAT & 0xc0000000) >> 30);
        MIi_GXDmaParams.fifoFunc = OS_GetIrqFunction(OS_IE_GXFIFO);

        G3X_SetFifoIntrCond(GX_FIFOINTR_COND_UNDERHALF);
        OS_SetIrqFunction(OS_IE_GXFIFO, MIi_FIFOCallback);
        (void)OS_EnableIrqMask(OS_IE_GXFIFO);

        MIi_FIFOCallback();

        (void)OS_RestoreInterrupts(lastIntrMode);
    }
}

ARM_FUNC static void MIi_FIFOCallback(void)
{
    if (!MIi_GXDmaParams.length)
    {
        return;
    }

    u32 length = (MIi_GXDmaParams.length >= MIi_GX_LENGTH_ONCE) ? MIi_GX_LENGTH_ONCE : MIi_GXDmaParams.length;
    u32 src = MIi_GXDmaParams.src;

    MIi_GXDmaParams.length -= length;
    MIi_GXDmaParams.src += length;

    if (!MIi_GXDmaParams.length)
    {
        OSi_EnterDmaCallback(MIi_GXDmaParams.dmaNo, MIi_DMACallback, NULL);
        MIi_DmaSetParams(MIi_GXDmaParams.dmaNo, src, (u32)REG_GXFIFO_ADDR, MI_CNT_SEND32_IF(length));
        (void)OS_ResetRequestIrqMask(OS_IE_GXFIFO);
    }
    else
    {
        MIi_DmaSetParams(MIi_GXDmaParams.dmaNo, src, (u32)REG_GXFIFO_ADDR, MI_CNT_SEND32(length));
        (void)OS_ResetRequestIrqMask(OS_IE_GXFIFO);
    }
}

ARM_FUNC static void MIi_DMACallback(void *arg)
{
#pragma unused(arg)
    (void)OS_DisableIrqMask(OS_IE_GXFIFO);

    G3X_SetFifoIntrCond(MIi_GXDmaParams.fifoCond);
    OS_SetIrqFunction(OS_IE_GXFIFO, MIi_GXDmaParams.fifoFunc);

    MIi_GXDmaParams.isBusy = FALSE;

    MIi_CallCallback(MIi_GXDmaParams.callback, MIi_GXDmaParams.arg);
}

ARM_FUNC void MI_SendGXCommandAsyncFast(u32 dmaNo, const void *src, u32 commandLength, MIDmaCallback callback, void *arg)
{
    if (!commandLength)
    {
        MIi_CallCallback(callback, arg);
        return;
    }

    while (MIi_GXDmaParams.isBusy) {}

    MIi_GXDmaParams.isBusy = TRUE;
    MIi_GXDmaParams.dmaNo = dmaNo;
    MIi_GXDmaParams.callback = callback;
    MIi_GXDmaParams.arg = arg;

    MIi_CheckAnotherAutoDMA(dmaNo, 0x38000000);

    MIi_CheckDma0SourceAddress(dmaNo, (u32)src, commandLength, DMA_SRC_INC);

    MI_WaitDma(dmaNo);

    OSi_EnterDmaCallback(dmaNo, MIi_DMAFastCallback, NULL);
    MIi_DmaSetParams(dmaNo, (u32)src, (u32)REG_GXFIFO_ADDR, MI_CNT_GXCOPY_IF(commandLength));
}

ARM_FUNC static void MIi_DMAFastCallback(void *arg)
{
#pragma unused(arg)
    MIi_GXDmaParams.isBusy = FALSE;

    MIi_CallCallback(MIi_GXDmaParams.callback, MIi_GXDmaParams.arg);
}