diff --git a/arch/arm-native/soc/broadcom/2708/dma/dma.conf b/arch/arm-native/soc/broadcom/2708/dma/dma.conf new file mode 100644 index 00000000000..6fa97031d9b --- /dev/null +++ b/arch/arm-native/soc/broadcom/2708/dma/dma.conf @@ -0,0 +1,17 @@ +##begin config +version 0.1 +residentpri 88 +libbase DMABase +libbasetype struct DMABase +##end config +##begin cdef +#include +##end cdef +##begin cdefprivate +#include "dma_private.h" +##end cdefprivate +##begin functionlist +int DMAAllocChannel(unsigned int flags) (D0) +void DMAFreeChannel(int channel) (D0) +int DMAWaitChannel(int channel, unsigned int timeout_us) (D0, D1) +##end functionlist diff --git a/arch/arm-native/soc/broadcom/2708/dma/dma_init.c b/arch/arm-native/soc/broadcom/2708/dma/dma_init.c new file mode 100644 index 00000000000..e4d66cf376d --- /dev/null +++ b/arch/arm-native/soc/broadcom/2708/dma/dma_init.c @@ -0,0 +1,336 @@ +/* + Copyright (C) 2026, The AROS Development Team. All rights reserved. +*/ + +#define DEBUG 0 + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dma_private.h" + +/* + * Channels the resource may hand out. The VideoCore firmware claims + * several channels for itself, we assume the following is available: + * + * Full engines (2, 4, 5) support 2D/TDMODE and wide bursts, lite engines + * do 32-bit transfers only. Lite channels 13-14 have no dedicated ARM + * IRQ line (only DMA0-12 map to GPU IRQ 16+N), so they are excluded — + * users may rely on per-channel completion interrupts. + */ +#define DMA_POOL_FULL ((1 << 2) | (1 << 4) | (1 << 5)) +#define DMA_POOL_LITE ((1 << 8) | (1 << 9) | (1 << 10) | (1 << 11) | \ + (1 << 12)) + +APTR KernelBase __attribute__((used)) = NULL; + +static int dma_init(struct DMABase *DMABase) +{ + D(bug("[DMA] %s()\n", __PRETTY_FUNCTION__)); + + KernelBase = OpenResource("kernel.resource"); + + if ((DMABase->dma_periiobase = KrnGetSystemAttr(KATTR_PeripheralBase)) == 0) + return FALSE; + + InitSemaphore(&DMABase->dma_Sem); + DMABase->dma_InUse = 0; + + D(bug("[DMA] %s: channel allocator initialised\n", __PRETTY_FUNCTION__)); + + return TRUE; +} + +/* 1 MHz system timer for wait deadlines. */ +static inline ULONG dma_now_us(struct DMABase *DMABase) +{ + return AROS_LE2LONG(*(volatile ULONG *)SYSTIMER_CLO); +} + +static void dma_zero(void *p, ULONG len) +{ + UBYTE *b = p; + while (len--) + *b++ = 0; +} + +/* + * Per-channel completion IRQ: W1C the INT flag (END is left for the + * waiter to consume) and signal the registered waiter. + */ +static void dma_irq_handler(void *data1, void *data2) +{ + struct DMABase *DMABase = (struct DMABase *)data1; + int channel = (int)(IPTR)data2; + volatile ULONG *cs = (volatile ULONG *)DMA_CS(channel); + + if (AROS_LE2LONG(*cs) & DMA_CS_INT) + { + struct Task *t; + + *cs = AROS_LONG2LE(DMA_CS_INT); + t = DMABase->dma_Wait[channel].waiter; + if (t) + Signal(t, 1UL << DMABase->dma_Wait[channel].sig); + } +} + +/* Enable the channel and bring the engine to a clean, idle state. */ +static void dma_channel_reset(struct DMABase *DMABase, int channel) +{ + volatile ULONG *enable = (volatile ULONG *)DMA_ENABLE_REG; + volatile ULONG *cs = (volatile ULONG *)DMA_CS(channel); + int try = 10000; + + *enable = AROS_LONG2LE(AROS_LE2LONG(*enable) | (1 << channel)); + + *cs = AROS_LONG2LE(DMA_CS_RESET); + while (try-- > 0) + { + if (!(AROS_LE2LONG(*cs) & DMA_CS_RESET)) + break; + } + *cs = AROS_LONG2LE(DMA_CS_INT | DMA_CS_END); +} + +AROS_LH1(int, DMAAllocChannel, + AROS_LHA(unsigned int, flags, D0), + struct DMABase *, DMABase, 1, Dma) +{ + AROS_LIBFUNC_INIT + + ULONG avail; + int channel = -1; + int ch; + + D(bug("[DMA] %s(0x%x)\n", __PRETTY_FUNCTION__, flags)); + + ObtainSemaphore(&DMABase->dma_Sem); + + /* Prefer lite channels so the scarce full engines stay available + * for users that need TDMODE. */ + if (flags & DMACHF_TDMODE) + avail = DMA_POOL_FULL & ~DMABase->dma_InUse; + else + { + avail = DMA_POOL_LITE & ~DMABase->dma_InUse; + if (avail == 0) + avail = DMA_POOL_FULL & ~DMABase->dma_InUse; + } + + if (avail != 0) + { + for (ch = 0; ch < 15; ch++) + { + if (avail & (1 << ch)) + { + channel = ch; + break; + } + } + + DMABase->dma_InUse |= (1 << channel); + dma_channel_reset(DMABase, channel); + + /* Completion IRQ for DMAWaitChannel — opt-in: drivers that run + * their own handler on the channel's line (the AHI drivers, with + * per-CB interrupts) must own it exclusively, or the two handlers + * race to W1C the INT flag and loses events. */ + DMABase->dma_Wait[channel].waiter = NULL; + DMABase->dma_Wait[channel].irq_handle = NULL; + if (flags & DMACHF_IRQ) + DMABase->dma_Wait[channel].irq_handle = + KrnAddIRQHandler(IRQ_DMA0 + channel, dma_irq_handler, + DMABase, (void *)(IPTR)channel); + } + + ReleaseSemaphore(&DMABase->dma_Sem); + + D(bug("[DMA] %s: allocated channel %d\n", __PRETTY_FUNCTION__, channel)); + + return channel; + + AROS_LIBFUNC_EXIT +} + +AROS_LH1(void, DMAFreeChannel, + AROS_LHA(int, channel, D0), + struct DMABase *, DMABase, 2, Dma) +{ + AROS_LIBFUNC_INIT + + D(bug("[DMA] %s(%d)\n", __PRETTY_FUNCTION__, channel)); + + if ((channel < 0) || (channel > 14)) + return; + + ObtainSemaphore(&DMABase->dma_Sem); + + if (DMABase->dma_InUse & (1 << channel)) + { + volatile ULONG *enable = (volatile ULONG *)DMA_ENABLE_REG; + volatile ULONG *cs = (volatile ULONG *)DMA_CS(channel); + int try = 10000; + + *cs = AROS_LONG2LE(DMA_CS_RESET); + while (try-- > 0) + { + if (!(AROS_LE2LONG(*cs) & DMA_CS_RESET)) + break; + } + + *enable = AROS_LONG2LE(AROS_LE2LONG(*enable) & ~(1 << channel)); + DMABase->dma_InUse &= ~(1 << channel); + + if (DMABase->dma_Wait[channel].irq_handle) + { + KrnRemIRQHandler(DMABase->dma_Wait[channel].irq_handle); + DMABase->dma_Wait[channel].irq_handle = NULL; + } + DMABase->dma_Wait[channel].waiter = NULL; + } + + ReleaseSemaphore(&DMABase->dma_Sem); + + AROS_LIBFUNC_EXIT +} + +/* + * Wait for the channel's current transfer to complete. The control + * block must have DMA_TI_INTEN set for the IRQ fast path; without it + * (or before timer.device is up) the wait degrades to a bounded poll. + * Returns 0 when the transfer ended normally (INT|END cleared), -1 on + * early stop or timeout (channel reset). + */ +AROS_LH2(int, DMAWaitChannel, + AROS_LHA(int, channel, D0), + AROS_LHA(unsigned int, timeout_us, D1), + struct DMABase *, DMABase, 3, Dma) +{ + AROS_LIBFUNC_INIT + + volatile ULONG *cs; + ULONG start; + BYTE dsig = -1; + BYTE tsig = -1; + int ret = -1; + + if ((channel < 0) || (channel > 14) || + !(DMABase->dma_InUse & (1 << channel))) + return -1; + + cs = (volatile ULONG *)DMA_CS(channel); + + /* Lazily open timer.device for the safety pulse (it doesn't exist + * yet when the resource initialises). */ + if (!DMABase->dma_TimerTried) + { + ObtainSemaphore(&DMABase->dma_Sem); + if (!DMABase->dma_TimerTried) + { + struct timerequest *tt = &DMABase->dma_TimerTemplate; + + tt->tr_node.io_Message.mn_Node.ln_Type = NT_MESSAGE; + tt->tr_node.io_Message.mn_Length = sizeof(*tt); + if (OpenDevice("timer.device", UNIT_MICROHZ, + (struct IORequest *)tt, 0) == 0) + DMABase->dma_TimerOk = TRUE; + DMABase->dma_TimerTried = TRUE; + } + ReleaseSemaphore(&DMABase->dma_Sem); + } + + /* Sample the deadline only after the (possibly slow) one-shot timer + * open, so its latency isn't charged against the caller's timeout. */ + start = dma_now_us(DMABase); + + if (DMABase->dma_Wait[channel].irq_handle && + (dsig = AllocSignal(-1)) >= 0) + { + DMABase->dma_Wait[channel].sig = dsig; + __asm__ __volatile__("dmb" ::: "memory"); + DMABase->dma_Wait[channel].waiter = FindTask(NULL); + + /* One reusable timer signal for the whole wait — the safety + * pulse re-arms each iteration but the bit is allocated once. */ + if (DMABase->dma_TimerOk) + tsig = AllocSignal(-1); + } + + for (;;) + { + ULONG v = AROS_LE2LONG(*cs); + + if (v & DMA_CS_END) + { + *cs = AROS_LONG2LE(DMA_CS_INT | DMA_CS_END); + ret = 0; + break; + } + if (!(v & DMA_CS_ACTIVE)) + { + *cs = AROS_LONG2LE(DMA_CS_RESET); + break; + } + if ((dma_now_us(DMABase) - start) > timeout_us) + { + *cs = AROS_LONG2LE(DMA_CS_RESET); + break; + } + + if (dsig >= 0 && tsig >= 0) + { + /* Sleep on the completion IRQ with a 4 ms timer as the + * safety pulse — a wedged channel (no END, no IRQ) still + * reaches the timeout instead of parking the task. */ + struct MsgPort port; + struct timerequest tr; + + dma_zero(&port, sizeof(port)); + port.mp_Node.ln_Type = NT_MSGPORT; + port.mp_Flags = PA_SIGNAL; + port.mp_SigBit = tsig; + port.mp_SigTask = FindTask(NULL); + NEWLIST(&port.mp_MsgList); + + tr = DMABase->dma_TimerTemplate; + tr.tr_node.io_Message.mn_Node.ln_Type = NT_MESSAGE; + tr.tr_node.io_Message.mn_ReplyPort = &port; + tr.tr_node.io_Message.mn_Length = sizeof(tr); + tr.tr_node.io_Command = TR_ADDREQUEST; + tr.tr_time.tv_secs = 0; + tr.tr_time.tv_micro = 4000; + + SendIO((struct IORequest *)&tr); + Wait((1UL << dsig) | (1UL << tsig)); + AbortIO((struct IORequest *)&tr); + WaitIO((struct IORequest *)&tr); + } + /* else: bounded poll until END/timeout */ + } + + if (dsig >= 0) + { + /* Stop the IRQ handler from signalling a bit we're about to free */ + Disable(); + DMABase->dma_Wait[channel].waiter = NULL; + __asm__ __volatile__("dmb" ::: "memory"); + if (tsig >= 0) + FreeSignal(tsig); + FreeSignal(dsig); + Enable(); + } + + return ret; + + AROS_LIBFUNC_EXIT +} + +ADD2INITLIB(dma_init, 0) diff --git a/arch/arm-native/soc/broadcom/2708/dma/dma_private.h b/arch/arm-native/soc/broadcom/2708/dma/dma_private.h new file mode 100644 index 00000000000..92348f07d9e --- /dev/null +++ b/arch/arm-native/soc/broadcom/2708/dma/dma_private.h @@ -0,0 +1,41 @@ +/* + Copyright (C) 2026, The AROS Development Team. All rights reserved. +*/ + +#ifndef DMA_PRIVATE_H_ +#define DMA_PRIVATE_H_ + +#include +#include +#include +#include +#include + +/* Per-channel completion-IRQ state. Each DMA channel has a dedicated + * ARM IRQ line (IRQ_DMA0 + channel); the handler W1Cs the INT flag and + * signals the registered waiter. */ +struct DMAChWait { + APTR irq_handle; + struct Task * volatile waiter; + volatile BYTE sig; +}; + +struct DMABase { + struct Node dma_Node; + struct SignalSemaphore dma_Sem; + unsigned int dma_periiobase; + unsigned int dma_InUse; /* Bitmask of allocated channels */ + + struct DMAChWait dma_Wait[15]; + + /* Lazily opened timer.device (UNIT_MICROHZ) template for the wait + * safety pulse — cloned into stack requests per use. */ + struct timerequest dma_TimerTemplate; + BOOL dma_TimerOk; + BOOL dma_TimerTried; +}; + +#define ARM_PERIIOBASE DMABase->dma_periiobase +#include + +#endif /* DMA_PRIVATE_H_ */ diff --git a/arch/arm-native/soc/broadcom/2708/dma/mmakefile.src b/arch/arm-native/soc/broadcom/2708/dma/mmakefile.src new file mode 100644 index 00000000000..7ed45a64f4f --- /dev/null +++ b/arch/arm-native/soc/broadcom/2708/dma/mmakefile.src @@ -0,0 +1,6 @@ + +include $(SRCDIR)/config/aros.cfg + +%build_module mmake=kernel-dma-bcm2708 \ + modname=dma modtype=resource \ + files="dma_init" diff --git a/arch/arm-native/soc/broadcom/2708/include/hardware/bcm2708_dma.h b/arch/arm-native/soc/broadcom/2708/include/hardware/bcm2708_dma.h new file mode 100644 index 00000000000..f1e1d3f302d --- /dev/null +++ b/arch/arm-native/soc/broadcom/2708/include/hardware/bcm2708_dma.h @@ -0,0 +1,46 @@ +/* + Copyright (C) 2026, The AROS Development Team. All rights reserved. + + BCM2835/BCM2708 DMA controller — shared definitions for users of + dma.resource. Register and TI/CS bit definitions live in bcm2708.h. +*/ + +#ifndef HARDWARE_BCM2708_DMA_H +#define HARDWARE_BCM2708_DMA_H + +#include + +/* + * Channel allocation flags for DMAAllocChannel() (dma.resource). + * + * BCM283x has full-featured engines (channels 0-6: 2D/TDMODE, wide + * bursts) and "lite" engines (channels 7-14: 32-bit transfers only, no + * TDMODE, lower throughput). The VideoCore firmware reserves several, so + * the resource only hands out a subset (full: 2, 4, 5; lite: 8-12). Lite + * channels are handed out first unless DMACHF_TDMODE is requested, + * keeping the scarce full engines available for users that need 2D + * stride mode. + */ +#define DMACHF_TDMODE (1 << 0) /* Needs a full engine (2D stride mode) */ +#define DMACHF_IRQ (1 << 1) /* Resource owns the channel IRQ for + * DMAWaitChannel(). Leave unset when the + * driver installs its own handler (AHI). */ + +/* DMA control block — hardware-defined layout, must be 32-byte aligned. + * All fields are little-endian; callers convert with AROS_LONG2LE. */ +struct BCM2708DMACB +{ + ULONG ti; /* Transfer information */ + ULONG source_ad; /* Source bus address */ + ULONG dest_ad; /* Destination bus address */ + ULONG txfr_len; /* Transfer length (2D mode: YLENGTH<<16 | XLENGTH) */ + ULONG stride; /* 2D mode stride (signed 16-bit pair) */ + ULONG nextconbk; /* Next control block bus address, 0 = stop */ + ULONG reserved[2]; +}; + +/* System RAM must be presented to the DMA engine through the uncached + * VideoCore bus alias. */ +#define BCM2708_DMA_BUS_ADDR(x) (0xC0000000 | (ULONG)(x)) + +#endif /* HARDWARE_BCM2708_DMA_H */ diff --git a/arch/arm-native/soc/broadcom/2708/include/hardware/sdhost.h b/arch/arm-native/soc/broadcom/2708/include/hardware/sdhost.h index fae56f8af4d..4fc030828f7 100644 --- a/arch/arm-native/soc/broadcom/2708/include/hardware/sdhost.h +++ b/arch/arm-native/soc/broadcom/2708/include/hardware/sdhost.h @@ -107,13 +107,10 @@ /* SDHOST timing derives from the VideoCore core clock. */ #define VCCLOCK_CORE 4 -/* DMA parameters chosen by the AROS driver. */ -#define SDHOST_DMA_CHANNEL 10 /* Lite channel avoids firmware-owned full channels */ +/* DMA parameters chosen by the AROS driver. The channel itself is + * allocated at runtime from dma.resource. */ #define SDHOST_DMA_DREQ 13 /* BCM2835 DREQ map ID for SDHOST */ -/* System RAM must be presented to DMA through the uncached VC alias. */ -#define SDHOST_DMA_BUS_ALIAS(x) (0xC0000000 | (ULONG)(x)) - /* DMA sees peripherals through the VideoCore bus aperture. */ #define SDHOST_SDDATA_DMA_ADDR (BCM2835_PERIBUSBASE + 0x202000 + SDDATA) diff --git a/arch/arm-native/soc/broadcom/2708/mmakefile.src b/arch/arm-native/soc/broadcom/2708/mmakefile.src index 91d92417f4c..0c9e70363f6 100644 --- a/arch/arm-native/soc/broadcom/2708/mmakefile.src +++ b/arch/arm-native/soc/broadcom/2708/mmakefile.src @@ -12,6 +12,7 @@ BCM2708_SP := aros-$(AROS_TARGET_CPU)-bcm2708.rom #MM kernel-timer \ #MM kernel-gpio-bcm2708 \ #MM kernel-mbox-bcm2708 \ +#MM kernel-dma-bcm2708 \ #MM kernel-sdcard \ #MM hidd-i2c-bcm2708 \ #MM hidd-vc4gfx \ @@ -22,7 +23,7 @@ BCM2708_SP := aros-$(AROS_TARGET_CPU)-bcm2708.rom PKG_LIBS := PKG_LIBS_ARCH := -PKG_RSRC := gpio mbox +PKG_RSRC := gpio mbox dma PKG_RSRC_ARCH := PKG_DEVS := sdcard USBHardware/usb2otg PKG_DEVS_ARCH := timer diff --git a/arch/arm-native/soc/broadcom/2708/sdcard/mmakefile.src b/arch/arm-native/soc/broadcom/2708/sdcard/mmakefile.src index 859556e9adb..8c05cde87cf 100644 --- a/arch/arm-native/soc/broadcom/2708/sdcard/mmakefile.src +++ b/arch/arm-native/soc/broadcom/2708/sdcard/mmakefile.src @@ -1,6 +1,8 @@ include $(SRCDIR)/config/aros.cfg +#MM- kernel-sdcard : kernel-dma-bcm2708-includes + USER_INCLUDES := -I$(SRCDIR)/$(CURDIR) -I$(SRCDIR)/rom/devs/sdcard # Arasan SDHCI controller (default) diff --git a/arch/arm-native/soc/broadcom/2708/sdcard/sdcard_sdhost_bus.c b/arch/arm-native/soc/broadcom/2708/sdcard/sdcard_sdhost_bus.c index cc41df49cff..8467af043d4 100644 --- a/arch/arm-native/soc/broadcom/2708/sdcard/sdcard_sdhost_bus.c +++ b/arch/arm-native/soc/broadcom/2708/sdcard/sdcard_sdhost_bus.c @@ -35,6 +35,7 @@ #include #include +#include #include #include @@ -65,7 +66,7 @@ static inline void sdhost_dsb(void) { asm volatile("dsb" ::: "memory"); } static inline ULONG sdhost_bus_addr(struct sdcard_Bus *bus, APTR virt) { struct SDCardBase *SDCardBase = bus->sdcb_DeviceBase; - return SDHOST_DMA_BUS_ALIAS((ULONG)(IPTR)KrnVirtualToPhysical(virt)); + return BCM2708_DMA_BUS_ADDR((ULONG)(IPTR)KrnVirtualToPhysical(virt)); } /* Fast bounce-buffer copy: 64 bytes/iteration via NEON for the bulk, @@ -144,13 +145,13 @@ static void sdhost_dma_setup(struct sdcard_Bus *bus, APTR buf, ULONG len, BOOL is_read) { struct sdhost_private *priv = SDHOST_PRIV(bus); - struct SDHostDMACB *cb = priv->dma_cb; + struct BCM2708DMACB *cb = priv->dma_cb; ULONG buf_bus = sdhost_bus_addr(bus, buf); ULONG ti; ULONG i; - /* Channel 10 is a "lite" DMA channel — 32-bit transfers only. - * Lite channels do not support the 128-bit WIDTH bit, so use + /* dma.resource hands out a "lite" DMA channel — 32-bit transfers + * only. Lite channels do not support the 128-bit WIDTH bit, so use * NO_WIDE_BURSTS and a 4-beat burst length instead. WAIT_RESP is * set for both directions to keep DMA in step with peripheral DREQ * pacing. */ @@ -198,17 +199,17 @@ static void sdhost_dma_setup(struct sdcard_Bus *bus, APTR buf, /* Reset DMA channel; loading a fresh CB after reset is the safest * way to clear any residual ACTIVE/INT/END state. */ - *(volatile ULONG *)DMA_CS(SDHOST_DMA_CHANNEL) = AROS_LONG2LE(DMA_CS_RESET); + *(volatile ULONG *)DMA_CS(priv->dma_channel) = AROS_LONG2LE(DMA_CS_RESET); sdhost_dsb(); for (i = 0; i < 100; i++) { - if (!(AROS_LE2LONG(*(volatile ULONG *)DMA_CS(SDHOST_DMA_CHANNEL)) & DMA_CS_RESET)) + if (!(AROS_LE2LONG(*(volatile ULONG *)DMA_CS(priv->dma_channel)) & DMA_CS_RESET)) break; } - *(volatile ULONG *)DMA_CS(SDHOST_DMA_CHANNEL) = + *(volatile ULONG *)DMA_CS(priv->dma_channel) = AROS_LONG2LE(DMA_CS_INT | DMA_CS_END); - *(volatile ULONG *)DMA_CONBLK_AD(SDHOST_DMA_CHANNEL) = - AROS_LONG2LE(SDHOST_DMA_BUS_ALIAS((ULONG)cb)); + *(volatile ULONG *)DMA_CONBLK_AD(priv->dma_channel) = + AROS_LONG2LE(BCM2708_DMA_BUS_ADDR((ULONG)cb)); sdhost_dsb(); } @@ -216,7 +217,7 @@ static inline void sdhost_dma_kick(struct sdcard_Bus *bus) { struct sdhost_private *priv = SDHOST_PRIV(bus); - *(volatile ULONG *)DMA_CS(SDHOST_DMA_CHANNEL) = AROS_LONG2LE( + *(volatile ULONG *)DMA_CS(priv->dma_channel) = AROS_LONG2LE( DMA_CS_WAIT_FOR_WRITES | DMA_CS_PANIC_PRI(15) | DMA_CS_PRI(8) | @@ -226,10 +227,14 @@ static inline void sdhost_dma_kick(struct sdcard_Bus *bus) static int sdhost_dma_wait(struct sdcard_Bus *bus, ULONG timeout_us) { - volatile ULONG *dma_cs = (volatile ULONG *)DMA_CS(SDHOST_DMA_CHANNEL); - /* Poll on 50 µs intervals — SD transfers run in the millisecond - * range so this is well below the minimum transfer time but - * 50× less CPU spin than per-microsecond polling. */ + /* Deliberately a bounded poll, NOT DMAWaitChannel: sleeping here + * lets other tasks run mid-transfer, which both (a) speculatively + * refetches destination cache lines during direct-to-buffer reads + * and (b) lets shared edge lines go dirty, so the post-DMA ClearD + * writes stale data over the DMA result — random disk corruption. + * The tight poll keeps the window closed, as it always did. */ + struct sdhost_private *priv = SDHOST_PRIV(bus); + volatile ULONG *dma_cs = (volatile ULONG *)DMA_CS(priv->dma_channel); const ULONG poll_step = 50; while (timeout_us > 0) @@ -243,9 +248,8 @@ static int sdhost_dma_wait(struct sdcard_Bus *bus, ULONG timeout_us) } if (!(cs & DMA_CS_ACTIVE)) { - bug("[SDHost%02u] DMA stopped early: CS=%08x SDHSTS=%08x SDEDM=%08x\n", - bus->sdcb_BusNum, cs, - sdhost_read(bus, SDHSTS), sdhost_read(bus, SDEDM)); + bug("[SDHost%02u] DMA stopped early: CS=%08x SDHSTS=%08x\n", + bus->sdcb_BusNum, cs, sdhost_read(bus, SDHSTS)); *dma_cs = AROS_LONG2LE(DMA_CS_RESET); sdhost_dsb(); return -1; @@ -253,15 +257,14 @@ static int sdhost_dma_wait(struct sdcard_Bus *bus, ULONG timeout_us) sdcard_Udelay(poll_step); timeout_us = (timeout_us > poll_step) ? timeout_us - poll_step : 0; } - bug("[SDHost%02u] DMA timeout: CS=%08x SDHSTS=%08x SDEDM=%08x SDCMD=%04x\n", - bus->sdcb_BusNum, AROS_LE2LONG(*dma_cs), - sdhost_read(bus, SDHSTS), sdhost_read(bus, SDEDM), - sdhost_read(bus, SDCMD)); + bug("[SDHost%02u] DMA timeout: SDHSTS=%08x\n", + bus->sdcb_BusNum, sdhost_read(bus, SDHSTS)); *dma_cs = AROS_LONG2LE(DMA_CS_RESET); sdhost_dsb(); return -1; } + /* ---------------------------------------------------------------------- */ /* SoftReset — bring registers to a known state and power up. */ /* Mirrors NetBSD's sdhost_host_reset. */ @@ -533,7 +536,7 @@ ULONG FNAME_SDHOSTBUS(SendCmd)(struct TagItem *CmdTags, struct sdcard_Bus *bus) * issue cleanly. */ if (has_data && retval != 0) { - *(volatile ULONG *)DMA_CS(SDHOST_DMA_CHANNEL) = AROS_LONG2LE(DMA_CS_RESET); + *(volatile ULONG *)DMA_CS(SDHOST_PRIV(bus)->dma_channel) = AROS_LONG2LE(DMA_CS_RESET); sdhost_dsb(); sdhost_flush_fifo(bus); } diff --git a/arch/arm-native/soc/broadcom/2708/sdcard/sdcard_sdhost_init.c b/arch/arm-native/soc/broadcom/2708/sdcard/sdcard_sdhost_init.c index 14abdbabdb3..5e0c4cf0267 100644 --- a/arch/arm-native/soc/broadcom/2708/sdcard/sdcard_sdhost_init.c +++ b/arch/arm-native/soc/broadcom/2708/sdcard/sdcard_sdhost_init.c @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -34,6 +35,7 @@ #define VCMB_PROPCHAN 8 APTR MBoxBase; +APTR DMABase; IPTR __arm_periiobase __attribute__((used)) = 0; @@ -141,6 +143,12 @@ static int FNAME_SDHOST(SDHostInit)(struct SDCardBase *SDCardBase) goto sdhost_fail; } + if ((DMABase = OpenResource("dma.resource")) == NULL) + { + bug("[SDHost] %s: Failed to open dma.resource\n", __PRETTY_FUNCTION__); + goto sdhost_fail; + } + /* * Query core clock rate. * SDHOST uses the core clock (VCCLOCK_CORE = 4), not the SDHCI clock. @@ -184,22 +192,22 @@ static int FNAME_SDHOST(SDHostInit)(struct SDCardBase *SDCardBase) /* Allocate DMA control block (must be 32-byte aligned) */ { - ULONG cb_alloc_size = sizeof(struct SDHostDMACB) + 31; + ULONG cb_alloc_size = sizeof(struct BCM2708DMACB) + 31; APTR cb_raw = AllocMem(cb_alloc_size, MEMF_PUBLIC | MEMF_CLEAR); if (cb_raw) { - ULONG dma_enable; - priv->dma_cb_raw = cb_raw; priv->dma_cb_raw_size = cb_alloc_size; - priv->dma_cb = (struct SDHostDMACB *)(((IPTR)cb_raw + 31) & ~31); + priv->dma_cb = (struct BCM2708DMACB *)(((IPTR)cb_raw + 31) & ~31); - /* Enable DMA channel and reset it */ - dma_enable = AROS_LE2LONG(*(volatile ULONG *)DMA_ENABLE_REG); - *(volatile ULONG *)DMA_ENABLE_REG = AROS_LONG2LE( - dma_enable | (1 << SDHOST_DMA_CHANNEL)); - *(volatile ULONG *)DMA_CS(SDHOST_DMA_CHANNEL) = - AROS_LONG2LE(DMA_CS_RESET); + /* A lite channel is sufficient — SDHOST is DREQ-paced. */ + priv->dma_channel = DMAAllocChannel(0); + if (priv->dma_channel < 0) + { + bug("[SDHost] %s: No DMA channel available — driver requires DMA\n", + __PRETTY_FUNCTION__); + goto sdhost_fail; + } /* Allocate cache-line-aligned bounce buffer for DMA reads. * CacheClearE on unaligned buffers corrupts adjacent memory. */ @@ -223,7 +231,7 @@ static int FNAME_SDHOST(SDHostInit)(struct SDCardBase *SDCardBase) } D(bug("[SDHost] %s: DMA channel %d enabled (bounce=%p)\n", - __PRETTY_FUNCTION__, SDHOST_DMA_CHANNEL, priv->dma_bounce)); + __PRETTY_FUNCTION__, priv->dma_channel, priv->dma_bounce)); } else { diff --git a/arch/arm-native/soc/broadcom/2708/sdcard/sdcard_sdhost_intern.h b/arch/arm-native/soc/broadcom/2708/sdcard/sdcard_sdhost_intern.h index bf30ee1a88e..4f2e7591069 100644 --- a/arch/arm-native/soc/broadcom/2708/sdcard/sdcard_sdhost_intern.h +++ b/arch/arm-native/soc/broadcom/2708/sdcard/sdcard_sdhost_intern.h @@ -18,6 +18,7 @@ extern IPTR __arm_periiobase; #define ARM_PERIIOBASE __arm_periiobase #include +#include #include #include "sdcard_base.h" @@ -25,17 +26,6 @@ extern IPTR __arm_periiobase; #define FNAME_SDHOST(x) SDHOST__Device__ ## x #define FNAME_SDHOSTBUS(x) SDHOST__SDBus__ ## x -/* DMA control block — must be 32-byte aligned */ -struct SDHostDMACB { - ULONG ti; /* Transfer Information */ - ULONG source_ad; /* Source address (bus address) */ - ULONG dest_ad; /* Destination address (bus address) */ - ULONG txfr_len; /* Transfer length in bytes */ - ULONG stride; /* 2D stride (unused) */ - ULONG nextconbk; /* Next CB address (bus address), 0 = stop */ - ULONG reserved[2]; /* Padding to 32 bytes */ -}; - /* Private data stored in sdcb_Private (cast to struct sdhost_private *) */ struct sdhost_private { ULONG max_clk; /* Core clock rate from VideoCore */ @@ -43,8 +33,9 @@ struct sdhost_private { ULONG hcfg; /* Cached SDHCFG value */ /* DMA state */ + LONG dma_channel; /* Channel allocated from dma.resource */ APTR dma_cb_raw; /* Unaligned alloc for CB */ - struct SDHostDMACB *dma_cb; /* 32-byte aligned control block */ + struct BCM2708DMACB *dma_cb; /* 32-byte aligned control block */ ULONG dma_cb_raw_size; /* Allocation size */ /* Bounce buffer used for DMA reads into possibly mis-aligned caller buffers. */ diff --git a/arch/arm-raspi/boot/mmakefile.src b/arch/arm-raspi/boot/mmakefile.src index 4f655cd6e5b..46c1a3464d4 100644 --- a/arch/arm-raspi/boot/mmakefile.src +++ b/arch/arm-raspi/boot/mmakefile.src @@ -73,6 +73,7 @@ ARM_BSP := aros-$(AROS_TARGET_CPU)-bsp.rom #MM kernel-fs-ram-quick \ #MM kernel-processor-quick \ #MM kernel-mbox-bcm2708-quick \ +#MM kernel-dma-bcm2708-quick \ #MM hidd-i2c-quick \ #MM kernel-hidd-gfx-quick \ #MM hidd-vc4gfx-quick \ @@ -123,6 +124,7 @@ ARM_BSP := aros-$(AROS_TARGET_CPU)-bsp.rom #MM kernel-fs-ram \ #MM kernel-processor \ #MM kernel-mbox-bcm2708 \ +#MM kernel-dma-bcm2708 \ #MM kernel-sdcard \ #MM hidd-i2c \ #MM hidd-i2c-bcm2708 \ @@ -145,7 +147,7 @@ RASPIFW_FILES := LICENCE.broadcom bootcode.bin fixup.dat start.elf bcm2709-r PKG_LIBS := aros partition utility oop graphics layers intuition keymap dos poseidon cgxbootpic gadtools lowlevel PKG_LIBS_ARCH := expansion -PKG_RSRC := openfirmware misc bootloader dosboot lddemon usbromstartup FileSystem shell shellcommands mbox +PKG_RSRC := openfirmware misc bootloader dosboot lddemon usbromstartup FileSystem shell shellcommands mbox dma PKG_RSRC_ARCH := processor PKG_DEVS := input gameport keyboard console sdcard USBHardware/usb2otg PKG_DEVS_ARCH := timer diff --git a/workbench/devs/AHI/Drivers/RPiHDMI/DriverData.h b/workbench/devs/AHI/Drivers/RPiHDMI/DriverData.h index 76ffabd17bb..ae2389fd9ca 100644 --- a/workbench/devs/AHI/Drivers/RPiHDMI/DriverData.h +++ b/workbench/devs/AHI/Drivers/RPiHDMI/DriverData.h @@ -7,18 +7,8 @@ #include "DriverBase.h" -/* - * BCM2835 DMA Control Block - must be 32-byte aligned - */ -struct DMAControlBlock { - ULONG ti; /* Transfer Information */ - ULONG source_ad; /* Source address (bus address) */ - ULONG dest_ad; /* Destination address (bus address) */ - ULONG txfr_len; /* Transfer length in bytes */ - ULONG stride; /* 2D stride */ - ULONG nextconbk; /* Next CB address (bus address), 0 = stop */ - ULONG reserved[2]; /* Padding to 32 bytes */ -}; +/* Shared BCM2835 DMA control block layout (32-byte aligned). */ +#include /* * Driver library base @@ -48,11 +38,11 @@ struct RPiHDMIData { /* Hardware state */ ULONG periiobase; - ULONG dma_channel; + LONG dma_channel; /* Allocated from dma.resource, -1 = none */ /* DMA control blocks (32-byte aligned) */ - struct DMAControlBlock *cb_base; /* Allocated block (for free) */ - struct DMAControlBlock *cb[2]; /* Aligned pointers to CB A and CB B */ + struct BCM2708DMACB *cb_base; /* Allocated block (for free) */ + struct BCM2708DMACB *cb[2]; /* Aligned pointers to CB A and CB B */ /* Audio buffers */ APTR mixbuffer; /* AHI mix buffer (signed 16-bit) */ diff --git a/workbench/devs/AHI/Drivers/RPiHDMI/rpihdmi-hwaccess.c b/workbench/devs/AHI/Drivers/RPiHDMI/rpihdmi-hwaccess.c index 2fdc435b252..1fc7141ba5c 100644 --- a/workbench/devs/AHI/Drivers/RPiHDMI/rpihdmi-hwaccess.c +++ b/workbench/devs/AHI/Drivers/RPiHDMI/rpihdmi-hwaccess.c @@ -463,7 +463,7 @@ void dma_build_control_blocks(struct RPiHDMIData *dd, ULONG peribase) int i; for (i = 0; i < 2; i++) { - struct DMAControlBlock *cb = dd->cb[i]; + struct BCM2708DMACB *cb = dd->cb[i]; cb->ti = DMA_TI_INTEN | DMA_TI_WAIT_RESP | DMA_TI_DEST_DREQ | DMA_TI_SRC_INC | DMA_TI_BURST_LENGTH(2) | DMA_TI_PERMAP(DMA_DREQ_HDMI) | DMA_TI_NO_WIDE_BURSTS; @@ -481,9 +481,8 @@ void dma_build_control_blocks(struct RPiHDMIData *dd, ULONG peribase) void dma_setup(ULONG peribase, ULONG channel, ULONG cb_bus_addr) { ULONG dma_base = peribase + 0x007000 + channel * 0x100; - ULONG enable_addr = peribase + 0x007FF0; - wr32le(enable_addr, rd32le(enable_addr) | (1 << channel)); + /* The channel is already enabled by dma.resource at allocation. */ wr32le(dma_base + 0x00, DMA_CS_RESET); udelay(peribase, 10); wr32le(dma_base + 0x00, DMA_CS_INT | DMA_CS_END); diff --git a/workbench/devs/AHI/Drivers/RPiHDMI/rpihdmi-hwaccess.h b/workbench/devs/AHI/Drivers/RPiHDMI/rpihdmi-hwaccess.h index 03b4ec05f1b..66cdd450b6b 100644 --- a/workbench/devs/AHI/Drivers/RPiHDMI/rpihdmi-hwaccess.h +++ b/workbench/devs/AHI/Drivers/RPiHDMI/rpihdmi-hwaccess.h @@ -11,7 +11,7 @@ * On BCM2835/2836, ARM physical 0x00000000 maps to GPU bus 0xC0000000 * (uncached alias). */ -#define GPU_BUS_ADDR(x) (0xC0000000 | (ULONG) (x)) +#define GPU_BUS_ADDR(x) BCM2708_DMA_BUS_ADDR(x) /* Register access helpers (little-endian, with ARM memory barriers) */ static inline void __dsb(void) @@ -104,8 +104,7 @@ static inline void wr32le(ULONG addr, ULONG val) #define CRP_CFG_EXTERNAL_CTS_EN (1 << 24) #define CRP_CFG_N(x) ((x) & 0xFFFFF) -/* DMA channel to use for HDMI audio (channel 6, avoiding PWM on 5) */ -#define HDMI_DMA_CHANNEL 6 +/* The audio DMA channel is allocated at runtime from dma.resource. */ /* DMA DREQ peripheral map ID for HDMI audio */ #define DMA_DREQ_HDMI 17 diff --git a/workbench/devs/AHI/Drivers/RPiHDMI/rpihdmi-init.c b/workbench/devs/AHI/Drivers/RPiHDMI/rpihdmi-init.c index 8210d0c40fe..f178203c643 100644 --- a/workbench/devs/AHI/Drivers/RPiHDMI/rpihdmi-init.c +++ b/workbench/devs/AHI/Drivers/RPiHDMI/rpihdmi-init.c @@ -3,11 +3,13 @@ #include #include +#include #include "library.h" #include "DriverData.h" APTR KernelBase = NULL; +APTR DMABase = NULL; /****************************************************************************** ** Custom driver init ********************************************************* @@ -31,6 +33,13 @@ BOOL DriverInit(struct DriverBase *AHIsubBase) return FALSE; } + DMABase = OpenResource("dma.resource"); + + if (DMABase == NULL) { + Req("Unable to open 'dma.resource'.\n"); + return FALSE; + } + RPiHDMIBase->periiobase = KrnGetSystemAttr(KATTR_PeripheralBase); if (RPiHDMIBase->periiobase == 0) { diff --git a/workbench/devs/AHI/Drivers/RPiHDMI/rpihdmi-main.c b/workbench/devs/AHI/Drivers/RPiHDMI/rpihdmi-main.c index 8fa1ce18153..2a9eb373502 100644 --- a/workbench/devs/AHI/Drivers/RPiHDMI/rpihdmi-main.c +++ b/workbench/devs/AHI/Drivers/RPiHDMI/rpihdmi-main.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -67,12 +68,12 @@ _AHIsub_AllocAudio(struct TagItem *taglist, struct AHIAudioCtrlDrv *AudioCtrl, s dd->mastertask = (struct Process *) FindTask(NULL); dd->ahisubbase = RPiHDMIBase; dd->periiobase = RPiHDMIBase->periiobase; - dd->dma_channel = HDMI_DMA_CHANNEL; + dd->dma_channel = DMAAllocChannel(0); } else { return AHISF_ERROR; } - if (dd->mastersignal == -1) { + if (dd->mastersignal == -1 || dd->dma_channel < 0) { return AHISF_ERROR; } @@ -86,6 +87,8 @@ _AHIsub_AllocAudio(struct TagItem *taglist, struct AHIAudioCtrlDrv *AudioCtrl, s void _AHIsub_FreeAudio(struct AHIAudioCtrlDrv *AudioCtrl, struct DriverBase *AHIsubBase) { if (AudioCtrl->ahiac_DriverData != NULL) { + if (dd->dma_channel >= 0) + DMAFreeChannel(dd->dma_channel); FreeSignal(dd->mastersignal); FreeVec(AudioCtrl->ahiac_DriverData); AudioCtrl->ahiac_DriverData = NULL; @@ -159,23 +162,23 @@ _AHIsub_Start(ULONG flags, struct AHIAudioCtrlDrv *AudioCtrl, struct DriverBase * Allocate DMA control blocks. * Each CB is 32 bytes and must be 32-byte aligned. */ - cb_alloc_size = sizeof(struct DMAControlBlock) * 2 + 32; + cb_alloc_size = sizeof(struct BCM2708DMACB) * 2 + 32; cb_raw = AllocVec(cb_alloc_size, MEMF_CLEAR | MEMF_PUBLIC); if (cb_raw == NULL) return AHIE_NOMEM; - dd->cb_base = (struct DMAControlBlock *) cb_raw; + dd->cb_base = (struct BCM2708DMACB *) cb_raw; /* Align to 32 bytes */ cb_raw = (UBYTE *) (((ULONG) cb_raw + 31) & ~31); - dd->cb[0] = (struct DMAControlBlock *) cb_raw; - dd->cb[1] = (struct DMAControlBlock *) (cb_raw + sizeof(struct DMAControlBlock)); + dd->cb[0] = (struct BCM2708DMACB *) cb_raw; + dd->cb[1] = (struct BCM2708DMACB *) (cb_raw + sizeof(struct BCM2708DMACB)); /* Build the DMA control block chain */ dma_build_control_blocks(dd, dd->periiobase); /* Flush DMA control blocks and buffers from ARM data cache */ - CacheClearE(dd->cb[0], sizeof(struct DMAControlBlock) * 2, CACRF_ClearD); + CacheClearE(dd->cb[0], sizeof(struct BCM2708DMACB) * 2, CACRF_ClearD); CacheClearE(dd->dmabuf[0], buf_bytes, CACRF_ClearD); CacheClearE(dd->dmabuf[1], buf_bytes, CACRF_ClearD); diff --git a/workbench/devs/AHI/Drivers/RPiPWM/DriverData.h b/workbench/devs/AHI/Drivers/RPiPWM/DriverData.h index c8f7ca3366f..92b44ccc1c4 100644 --- a/workbench/devs/AHI/Drivers/RPiPWM/DriverData.h +++ b/workbench/devs/AHI/Drivers/RPiPWM/DriverData.h @@ -7,18 +7,8 @@ #include "DriverBase.h" -/* - * BCM2835 DMA Control Block - must be 32-byte aligned - */ -struct DMAControlBlock { - ULONG ti; /* Transfer Information */ - ULONG source_ad; /* Source address (bus address) */ - ULONG dest_ad; /* Destination address (bus address) */ - ULONG txfr_len; /* Transfer length in bytes */ - ULONG stride; /* 2D stride */ - ULONG nextconbk; /* Next CB address (bus address), 0 = stop */ - ULONG reserved[2]; /* Padding to 32 bytes */ -}; +/* Shared BCM2835 DMA control block layout (32-byte aligned). */ +#include /* * Driver library base @@ -48,11 +38,11 @@ struct RPiPWMData { /* Hardware state */ ULONG periiobase; - ULONG dma_channel; + LONG dma_channel; /* Allocated from dma.resource, -1 = none */ /* DMA control blocks (32-byte aligned) */ - struct DMAControlBlock *cb_base; /* Allocated block (for free) */ - struct DMAControlBlock *cb[2]; /* Aligned pointers to CB A and CB B */ + struct BCM2708DMACB *cb_base; /* Allocated block (for free) */ + struct BCM2708DMACB *cb[2]; /* Aligned pointers to CB A and CB B */ /* Audio buffers */ APTR mixbuffer; /* AHI mix buffer (signed 16-bit) */ diff --git a/workbench/devs/AHI/Drivers/RPiPWM/rpipwm-hwaccess.c b/workbench/devs/AHI/Drivers/RPiPWM/rpipwm-hwaccess.c index 7d7fc92f967..2048b3e1a52 100644 --- a/workbench/devs/AHI/Drivers/RPiPWM/rpipwm-hwaccess.c +++ b/workbench/devs/AHI/Drivers/RPiPWM/rpipwm-hwaccess.c @@ -222,7 +222,7 @@ void dma_build_control_blocks(struct RPiPWMData *dd, ULONG peribase) int i; for (i = 0; i < 2; i++) { - struct DMAControlBlock *cb = dd->cb[i]; + struct BCM2708DMACB *cb = dd->cb[i]; cb->ti = DMA_TI_INTEN | DMA_TI_WAIT_RESP | DMA_TI_DEST_DREQ | DMA_TI_SRC_INC | DMA_TI_PERMAP(DMA_DREQ_PWM) | DMA_TI_NO_WIDE_BURSTS; @@ -244,10 +244,8 @@ void dma_build_control_blocks(struct RPiPWMData *dd, ULONG peribase) void dma_setup(ULONG peribase, ULONG channel, ULONG cb_bus_addr) { ULONG dma_base = peribase + 0x007000 + channel * 0x100; - ULONG enable_addr = peribase + 0x007FF0; - /* Enable the DMA channel */ - wr32le(enable_addr, rd32le(enable_addr) | (1 << channel)); + /* The channel is already enabled by dma.resource at allocation. */ /* Reset the channel */ wr32le(dma_base + 0x00, DMA_CS_RESET); diff --git a/workbench/devs/AHI/Drivers/RPiPWM/rpipwm-hwaccess.h b/workbench/devs/AHI/Drivers/RPiPWM/rpipwm-hwaccess.h index 0f17998df68..8a5c98dce16 100644 --- a/workbench/devs/AHI/Drivers/RPiPWM/rpipwm-hwaccess.h +++ b/workbench/devs/AHI/Drivers/RPiPWM/rpipwm-hwaccess.h @@ -11,7 +11,7 @@ * On BCM2835/2836, ARM physical 0x00000000 maps to GPU bus 0xC0000000 * (uncached alias). */ -#define GPU_BUS_ADDR(x) (0xC0000000 | (ULONG) (x)) +#define GPU_BUS_ADDR(x) BCM2708_DMA_BUS_ADDR(x) /* Register access helpers (little-endian, with ARM memory barriers) */ static inline void __dsb(void) @@ -42,8 +42,7 @@ static inline void wr32le(ULONG addr, ULONG val) /* PWM range: 10-bit (1024 levels) gives decent audio quality */ #define PWM_AUDIO_RANGE 1024 -/* DMA channel to use for audio (channel 5, avoiding GPU-reserved 0-3) */ -#define PWM_DMA_CHANNEL 5 +/* The audio DMA channel is allocated at runtime from dma.resource. */ /* PLLD clock frequency (500 MHz on BCM2835/2836) */ #define PLLD_FREQ 500000000 diff --git a/workbench/devs/AHI/Drivers/RPiPWM/rpipwm-init.c b/workbench/devs/AHI/Drivers/RPiPWM/rpipwm-init.c index 96b82095465..62d6f7641b3 100644 --- a/workbench/devs/AHI/Drivers/RPiPWM/rpipwm-init.c +++ b/workbench/devs/AHI/Drivers/RPiPWM/rpipwm-init.c @@ -3,11 +3,13 @@ #include #include +#include #include "library.h" #include "DriverData.h" APTR KernelBase = NULL; +APTR DMABase = NULL; /****************************************************************************** ** Custom driver init ********************************************************* @@ -31,6 +33,13 @@ BOOL DriverInit(struct DriverBase *AHIsubBase) return FALSE; } + DMABase = OpenResource("dma.resource"); + + if (DMABase == NULL) { + Req("Unable to open 'dma.resource'.\n"); + return FALSE; + } + RPiPWMBase->periiobase = KrnGetSystemAttr(KATTR_PeripheralBase); if (RPiPWMBase->periiobase == 0) { diff --git a/workbench/devs/AHI/Drivers/RPiPWM/rpipwm-main.c b/workbench/devs/AHI/Drivers/RPiPWM/rpipwm-main.c index 26853e317f4..a495faafcef 100644 --- a/workbench/devs/AHI/Drivers/RPiPWM/rpipwm-main.c +++ b/workbench/devs/AHI/Drivers/RPiPWM/rpipwm-main.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -69,13 +70,13 @@ _AHIsub_AllocAudio(struct TagItem *taglist, struct AHIAudioCtrlDrv *AudioCtrl, s dd->mastertask = (struct Process *) FindTask(NULL); dd->ahisubbase = RPiPWMBase; dd->periiobase = RPiPWMBase->periiobase; - dd->dma_channel = PWM_DMA_CHANNEL; + dd->dma_channel = DMAAllocChannel(0); dd->pwm_range = PWM_AUDIO_RANGE; } else { return AHISF_ERROR; } - if (dd->mastersignal == -1) { + if (dd->mastersignal == -1 || dd->dma_channel < 0) { return AHISF_ERROR; } @@ -90,6 +91,8 @@ _AHIsub_AllocAudio(struct TagItem *taglist, struct AHIAudioCtrlDrv *AudioCtrl, s void _AHIsub_FreeAudio(struct AHIAudioCtrlDrv *AudioCtrl, struct DriverBase *AHIsubBase) { if (AudioCtrl->ahiac_DriverData != NULL) { + if (dd->dma_channel >= 0) + DMAFreeChannel(dd->dma_channel); FreeSignal(dd->mastersignal); FreeVec(AudioCtrl->ahiac_DriverData); AudioCtrl->ahiac_DriverData = NULL; @@ -170,17 +173,17 @@ _AHIsub_Start(ULONG flags, struct AHIAudioCtrlDrv *AudioCtrl, struct DriverBase * Each CB is 32 bytes and must be 32-byte aligned. * Allocate enough for 2 CBs + alignment padding. */ - cb_alloc_size = sizeof(struct DMAControlBlock) * 2 + 32; + cb_alloc_size = sizeof(struct BCM2708DMACB) * 2 + 32; cb_raw = AllocVec(cb_alloc_size, MEMF_CLEAR | MEMF_PUBLIC); if (cb_raw == NULL) return AHIE_NOMEM; - dd->cb_base = (struct DMAControlBlock *) cb_raw; + dd->cb_base = (struct BCM2708DMACB *) cb_raw; /* Align to 32 bytes */ cb_raw = (UBYTE *) (((ULONG) cb_raw + 31) & ~31); - dd->cb[0] = (struct DMAControlBlock *) cb_raw; - dd->cb[1] = (struct DMAControlBlock *) (cb_raw + sizeof(struct DMAControlBlock)); + dd->cb[0] = (struct BCM2708DMACB *) cb_raw; + dd->cb[1] = (struct BCM2708DMACB *) (cb_raw + sizeof(struct BCM2708DMACB)); /* Build the DMA control block chain */ dma_build_control_blocks(dd, dd->periiobase); @@ -190,7 +193,7 @@ _AHIsub_Start(ULONG flags, struct AHIAudioCtrlDrv *AudioCtrl, struct DriverBase * to physical RAM. The DMA engine reads via the GPU bus * (uncached alias 0xC0000000) and won't see cached data. */ - CacheClearE(dd->cb[0], sizeof(struct DMAControlBlock) * 2, CACRF_ClearD); + CacheClearE(dd->cb[0], sizeof(struct BCM2708DMACB) * 2, CACRF_ClearD); CacheClearE(dd->dmabuf[0], buf_bytes, CACRF_ClearD); CacheClearE(dd->dmabuf[1], buf_bytes, CACRF_ClearD);