Raw File
edmac-memcpy.c
#include "edmac-memcpy.h"
#include "dryos.h"
#include "edmac.h"

#ifdef CONFIG_EDMAC_MEMCPY

static struct semaphore * edmac_memcpy_sem = 0; /* to allow only one memcpy running at a time */
static struct semaphore * edmac_memcpy_done_sem = 0; /* to know when memcpy is finished */

static void edmac_memcpy_init()
{
    edmac_memcpy_sem = create_named_semaphore("edmac_memcpy_sem", 1);
    edmac_memcpy_done_sem = create_named_semaphore("edmac_memcpy_done_sem", 0);
}

INIT_FUNC("edmac_memcpy", edmac_memcpy_init);

static void edmac_memcpy_complete_cbr (int ctx)
{
    give_semaphore(edmac_memcpy_done_sem);
}

static void edmac_memcpy_null_cbr (int ctx)
{
}

void* edmac_copy_rectangle_adv_start(void* dst, void* src, int src_width, int src_x, int src_y, int dst_width, int dst_x, int dst_y, int w, int h)
{
    take_semaphore(edmac_memcpy_sem, 0);

    /* pick some free (check using debug menu) EDMAC channels write: 0x00-0x06, 0x10-0x16, 0x20-0x21. read: 0x08-0x0D, 0x18-0x1D,0x28-0x2B */
    uint32_t dmaChannelRead = 0x19;
    uint32_t dmaChannelWrite = 0x11;
    
    #if defined(CONFIG_5D2)
    dmaChannelWrite = 0x3;
    #elif defined(CONFIG_650D)
    dmaChannelWrite = 0x13;
    #endif

    /* both channels get connected to this... lets call it service. it will just output the data it gets as input */
    uint32_t dmaConnection = 6;

    /* see wiki, register map, EDMAC what the flags mean. they are for setting up copy block size */
    uint32_t dmaFlags = 0x20001000;

    /* create a memory suite from a already existing (continuous) memory block with given size. */
    uint32_t src_adjusted = ((uint32_t)src & 0x1FFFFFFF) + src_x + src_y * src_width;
    uint32_t dst_adjusted = ((uint32_t)dst & 0x1FFFFFFF) + dst_x + dst_y * dst_width;
    
    /* only read channel will emit a callback when reading from memory is done. write channels would just silently wrap */
    RegisterEDmacCompleteCBR(dmaChannelRead, &edmac_memcpy_complete_cbr, 0);
    RegisterEDmacAbortCBR(dmaChannelRead, &edmac_memcpy_complete_cbr, 0);
    RegisterEDmacPopCBR(dmaChannelRead, &edmac_memcpy_complete_cbr, 0);
    RegisterEDmacCompleteCBR(dmaChannelWrite, &edmac_memcpy_null_cbr, 0);
    RegisterEDmacAbortCBR(dmaChannelWrite, &edmac_memcpy_null_cbr, 0);
    RegisterEDmacPopCBR(dmaChannelWrite, &edmac_memcpy_null_cbr, 0);
    
    /* connect the selected channels to 6 so any data read from RAM is passed to write channel */
    ConnectWriteEDmac(dmaChannelWrite, dmaConnection);
    ConnectReadEDmac(dmaChannelRead, dmaConnection);

    /* xb is width */
    /* yb is height-1 */
    /* off1b is cropping (or padding) on the right side of the image */
    static struct edmac_info src_edmac_info;
    static struct edmac_info dst_edmac_info;
    
    memset(&src_edmac_info, sizeof(struct edmac_info), 0);
    memset(&dst_edmac_info, sizeof(struct edmac_info), 0);
    
    src_edmac_info.xb = w;
    src_edmac_info.yb = h-1;
    src_edmac_info.off1b = src_width - w;
    
    /* destination setup has no special cropping */
    dst_edmac_info.xb = w;
    dst_edmac_info.yb = h-1;
    dst_edmac_info.off1b = dst_width - w;
    
    SetEDmac(dmaChannelRead, (void*)src_adjusted, &src_edmac_info, dmaFlags);
    SetEDmac(dmaChannelWrite, (void*)dst_adjusted, &dst_edmac_info, dmaFlags);
    
    /* start transfer. no flags for write, 2 for read channels */
    StartEDmac(dmaChannelWrite, 0);
    StartEDmac(dmaChannelRead, 2);
    return dst;
}

void edmac_copy_rectangle_adv_finish()
{
    take_semaphore(edmac_memcpy_done_sem, 0);

    /* fixme: duplicate code */
    /* pick some free (check using debug menu) EDMAC channels write: 0x00-0x06, 0x10-0x16, 0x20-0x21. read: 0x08-0x0D, 0x18-0x1D,0x28-0x2B */
    uint32_t dmaChannelRead = 0x19;
    uint32_t dmaChannelWrite = 0x11;

    #if defined(CONFIG_5D2)
    dmaChannelWrite = 0x3;
    #elif defined(CONFIG_650D)
    dmaChannelWrite = 0x13;
    #endif
    /* duplicate code ends */

    /* set default CBRs again and stop both DMAs */
    UnregisterEDmacCompleteCBR(dmaChannelRead);
    UnregisterEDmacAbortCBR(dmaChannelRead);
    UnregisterEDmacPopCBR(dmaChannelRead);
    UnregisterEDmacCompleteCBR(dmaChannelWrite);
    UnregisterEDmacAbortCBR(dmaChannelWrite);
    UnregisterEDmacPopCBR(dmaChannelWrite);
    
    PopEDmac(dmaChannelRead);
    PopEDmac(dmaChannelWrite);

    give_semaphore(edmac_memcpy_sem);
}

void* edmac_copy_rectangle_adv(void* dst, void* src, int src_width, int src_x, int src_y, int dst_width, int dst_x, int dst_y, int w, int h)
{
    void* ans = edmac_copy_rectangle_adv_start(dst, src, src_width, src_x, src_y, dst_width, dst_x, dst_y, w, h);
    edmac_copy_rectangle_adv_finish();
    return ans;
}

void* edmac_copy_rectangle(void* dst, void* src, int src_width, int x, int y, int w, int h)
{
    return edmac_copy_rectangle_adv(dst, src, src_width, x, y, w, 0, 0, w, h);
}

void* edmac_copy_rectangle_start(void* dst, void* src, int src_width, int x, int y, int w, int h)
{
    return edmac_copy_rectangle_adv_start(dst, src, src_width, x, y, w, 0, 0, w, h);
}

void edmac_copy_rectangle_finish()
{
    return edmac_copy_rectangle_adv_finish();
}

void* edmac_memcpy(void* dst, void* src, size_t length)
{
    int h = length / 4096;
    return edmac_copy_rectangle_adv(dst, src, 4096, 0, 0, 4096, 0, 0, 4096, h);
}

void* edmac_memcpy_start(void* dst, void* src, size_t length)
{
    int h = length / 4096;
    return edmac_copy_rectangle_adv_start(dst, src, 4096, 0, 0, 4096, 0, 0, 4096, h);
}

void edmac_memcpy_finish()
{
    return edmac_copy_rectangle_adv_finish();
}

#endif
back to top