www.pudn.com > camera.rar > dma.c


// 
// Copyright (c) Microsoft Corporation.  All rights reserved. 
// 
// 
// Use of this sample source code is subject to the terms of the Microsoft 
// license agreement under which you licensed this sample source code. If 
// you did not accept the terms of the license agreement, you are not 
// authorized to use this sample source code. For the terms of the license, 
// please see the license agreement between you and Microsoft or, if applicable, 
// see the LICENSE.RTF on your install media or the root of your tools installation. 
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES. 
// 
// 
// (C) Copyright 2006 Marvell International Ltd. 
// All Rights Reserved 
// 
 
#include  
#include  
#include  
#include  
#include  
#include  
 
#include "qci.h" 
#include "qci_private.h" 
 
#define MAX_BUF_NUM 1200 
#define MAX_FRAG_NUM 10 
#define MAX_EMPTY_NUM 5 
 
#define SINGLE_DESCRIPTOR_TRANSFER_MAX  4096 
frame_list_t *current_frame_list; 
frame_t *video_frame_tail; 
int still_skips; 
 
static void frame_dma_link(frame_t *f1, frame_t *f2); 
static void free_frame(frame_t* frame); 
static frame_list_t* find_frame_list(format_t* format); 
static void frame_free_dma_buf(frame_t *frame); 
 
 
void DMABufAlloc(dma_buf_t *buf, UINT32 size, BOOL cache) 
{ 
    DMA_ADAPTER_OBJECT adapter; 
    PHYSICAL_ADDRESS   phy_addr; 
 
    adapter.ObjectSize    = sizeof (DMA_ADAPTER_OBJECT); 
    adapter.InterfaceType = Internal; 
    adapter.BusNumber     = 0; 
    buf->buf = HalAllocateCommonBuffer(&adapter, size, &phy_addr, FALSE); 
    buf->phy_addr = phy_addr.LowPart; 
    buf->size = size; 
     
    if (cache) 
        buf->buf = MmMapIoSpace(phy_addr, size, TRUE); 
     
    if (!buf->buf) 
        DEBUGMSG( ZONE_IOCTL, ( _T("CAM: qci failed to alloc buffer %d!\r\n"), size) );         
    else 
    DEBUGMSG( ZONE_IOCTL, ( _T("CAM: dma_buf_alloc: Size:%d     VirtualAddr:0x%x     PhyAddr:0x%x\r\n"),size,buf->buf,buf->phy_addr) ); 
     
} 
 
void DMABufFree(dma_buf_t *buf) 
{ 
    PHYSICAL_ADDRESS phy_addr; 
 
    if (buf->buf == NULL) 
        return; 
 
    memset(&phy_addr, 0, sizeof(phy_addr)); 
    HalFreeCommonBuffer(NULL, 0, phy_addr, (PVOID)buf->buf, 0); 
    buf->buf = NULL; 
} 
 
#define DMA_DESC_SIZE (sizeof(PXA_CI_DMAC_DESCRIPTOR_T)) 
 
 
// Add the buffer into the buffer pool and generate the DMA chains for the buffer 
static BOOL dma_chain_init(plane_t *plane, 
                           UINT32 dma_chain) 
{ 
    UINT32 i, n = 0; 
    UINT32 size = 0; 
    UINT32 remain = 0; 
    PXA_CI_DMAC_DESCRIPTOR_T *descs; 
    UINT32 descs_n; 
    UINT32 phy_addr; 
    UINT32 buf_phy_addr; 
 
    descs = (PXA_CI_DMAC_DESCRIPTOR_T*)plane->desc_buf.buf; 
    descs_n = plane->desc_buf.size / sizeof(*descs); 
    phy_addr = plane->desc_buf.phy_addr; 
    remain = plane->buf.size; 
    buf_phy_addr = plane->buf.phy_addr; 
 
    for (i = 0; i < descs_n; i++) 
    { 
        if (remain == 0) 
        { 
            DEBUGMSG( ZONE_IOCTL, ( _T("CAM: dma buffer is smaller than expected!!!\r\n")) ); 
            return FALSE; 
        } 
 
        size = remain > SINGLE_DESCRIPTOR_TRANSFER_MAX? 
                SINGLE_DESCRIPTOR_TRANSFER_MAX : remain; 
 
        phy_addr += sizeof(*descs); 
        descs[i].ddadr = phy_addr; 
        //descs[i].dsadr = PXA_CI_REGBASE_PHY + dma_chain; 
        descs[i].dsadr = dma_chain; 
        descs[i].dtadr = buf_phy_addr; 
        descs[i].dcmd = size | PXA_CI_DMAC_DCMD_INC_TRG_ADDR; 
 
        remain -= size; 
        buf_phy_addr += size; 
    } 
    return TRUE; 
} 
 
 
// calculate frame buffer sizes by frame format and dimension 
static void get_buffer_desc_size(int format, 
                          UINT32 width, 
                          UINT32 height, 
                          int plane_size[3], 
                          int n_descs[3]) 
{ 
    UINT32 frame_size; 
    int i; 
 
    plane_size[0] = 0; 
    plane_size[1] = 0; 
    plane_size[2] = 0; 
 
    switch(format)  
    { 
    case PXA_CAMERA_IMAGE_FORMAT_RAW10: 
    case PXA_CAMERA_IMAGE_FORMAT_RAW9: 
    case PXA_CAMERA_IMAGE_FORMAT_RGB565: 
    case PXA_CAMERA_IMAGE_FORMAT_YCBCR422_PACKED: 
        frame_size = width * height * 2; 
        plane_size[0] = frame_size; 
        break; 
    case PXA_CAMERA_IMAGE_FORMAT_RGB888_PACKED: 
        frame_size = width * height * 3; 
        plane_size[0] = frame_size; 
        break; 
    case PXA_CAMERA_IMAGE_FORMAT_YCBCR422_PLANAR: 
        frame_size = width * height * 2; 
        plane_size[0] = frame_size / 2; 
        plane_size[1] = frame_size / 4; 
        plane_size[2] = frame_size / 4; 
        break; 
    default: 
        break; 
    } 
 
    for (i = 0; i < 3; i++) 
        n_descs[i] = (plane_size[i] + SINGLE_DESCRIPTOR_TRANSFER_MAX - 1)  
                     / SINGLE_DESCRIPTOR_TRANSFER_MAX; 
} 
 
UINT32 round(UINT32 x, UINT32 y) 
{ 
    return ((x + y - 1) / y) * y; 
} 
 
#define MIN(x, y) ((x) > (y)? (y) : (x)) 
 
//static int chain_tbl[3] = { PXA_CIBR0, PXA_CIBR1, PXA_CIBR2 }; 
 
 
 
static BOOL frame_alloc_dma_buf(frame_t *frame) 
{ 
    int plane_size[3]; 
    int n_descs[3]; 
    int i; 
    UINT32 chain_tbl[3] = {g_pCIRegs->PXA_CIBR0, g_pCIRegs->PXA_CIBR1, g_pCIRegs->PXA_CIBR2}; 
	 
    format_t* format = &frame->list->format; 
   
    get_buffer_desc_size(format->format, format->width, format->height, 
                         plane_size, n_descs); 
 
    for (i = 0; i < 3; i++) 
    { 
        plane_t *plane = &frame->plane[i]; 
        int desc_buf_size; 
        int rev = 0; 
 
        if (!plane_size[i]) 
            break; 
 
        desc_buf_size = n_descs[i] * sizeof(PXA_CI_DMAC_DESCRIPTOR_T); 
        DMABufAlloc(&plane->desc_buf, desc_buf_size, 0); 
        if (!plane->desc_buf.buf) 
            goto clean; 
 
        DMABufAlloc(&plane->buf, round(plane_size[i], 16), 1); 
 
        if (!plane->buf.buf) 
            goto clean; 
 
        if (!dma_chain_init(plane, chain_tbl[i])) 
            goto clean; 
    } 
 
    return TRUE; 
 
clean: 
    frame_free_dma_buf(frame); 
    return FALSE; 
} 
 
static void frame_free_dma_buf(frame_t *frame) 
{ 
    UINT32 i; 
 
    for (i = 0; i < 3; i++) 
    { 
        plane_t *plane = &frame->plane[i]; 
 
        DMABufFree(&plane->buf); 
        DMABufFree(&plane->desc_buf); 
    } 
} 
 
static void frame_link(frame_t *f1, frame_t *f2) 
{ 
    f1->next = f2; 
    frame_dma_link(f1, f2); 
} 
 
// mark a frame before dma transfer. 
// if a mark is overwritten, it means the frame is ready 
const short dma_mark[4] = { 0x0, 0xffff, 0x0, 0xffff }; 
 
static char *get_mark_ptr(frame_t *frame) 
{ 
    plane_t *plane = &frame->plane[0]; 
    dma_buf_t *buf = &plane->buf; 
    return buf->buf + buf->size - sizeof(dma_mark); 
} 
 
static void frame_clean_cache(frame_t *frame) 
{ 
    UINT32 i; 
 
    for (i = 0; i < 3; i++) 
        CacheRangeFlush(frame->plane[i].buf.buf, frame->plane[i].buf.size, CACHE_SYNC_DISCARD); 
} 
static void frame_mark(frame_t *frame) 
{ 
    char *mark = get_mark_ptr(frame); 
 
    memcpy(mark, dma_mark, sizeof(dma_mark)); 
} 
 
static BOOL frame_check_mark(frame_t *frame) 
{ 
    char *mark = get_mark_ptr(frame); 
 
    return memcmp(mark, dma_mark, sizeof(dma_mark)) == 0; 
} 
 
static void frame_dma_link(frame_t *f1, frame_t *f2) 
{ 
    UINT32 i; 
 
    for (i = 0; i < 3; i++) 
    { 
        PXA_CI_DMAC_DESCRIPTOR_T *descs; 
        UINT32 descs_n; 
        plane_t *p1 = &f1->plane[i]; 
        plane_t *p2 = &f2->plane[i]; 
 
        if (!p1->buf.buf) 
            return; 
 
        descs = (PXA_CI_DMAC_DESCRIPTOR_T*)p1->desc_buf.buf; 
        descs_n = p1->desc_buf.size / sizeof(*descs); 
 
        descs[descs_n - 1].ddadr = p2->desc_buf.phy_addr; 
    } 
} 
 
static void frame_append(frame_t *frame) 
{ 
    if (!frame->list) 
        return; 
 
    frame->next = NULL; 
    frame_mark(frame); 
    frame_clean_cache(frame); 
    frame_dma_link(frame, frame); 
 
    if (!frame->list->head) 
        frame->list->head = frame; 
    else 
        frame_link(frame->list->tail, frame); 
 
    frame->list->tail = frame; 
} 
 
static frame_t *frame_take() 
{ 
    frame_t *frame; 
 
    frame = current_frame_list->head; 
 
    if (frame == NULL) 
        return NULL; 
 
    /* to keep DMA running 
    * the last frame should not be removed 
    */ 
    if (frame->next == NULL) 
        return NULL; 
 
    if (frame_check_mark(frame)) 
        return NULL; 
 
    current_frame_list->head = frame->next; 
 
    return frame; 
} 
 
static frame_t* alloc_frame(frame_list_t *list) 
{ 
    frame_t *frame; 
 
    frame = malloc(sizeof(frame_t)); 
     
    if (frame == NULL) 
        return FALSE; 
 
    memset(frame, 0, sizeof(frame_t)); 
 
    frame->list = list; 
 
    if (!frame_alloc_dma_buf(frame)) 
            goto err_alloc_frame; 
 
    return frame; 
 
err_alloc_frame: 
    free_frame(frame); 
    return NULL; 
} 
 
static void free_frame(frame_t* frame) 
{ 
    frame_free_dma_buf(frame); 
    free(frame); 
} 
 
static void free_frame_list(frame_list_t* list) 
{ 
    frame_t* i; 
    frame_t* next; 
    for (i = list->head; i; i = next) 
    { 
        next = i->next; 
        free_frame(i); 
    } 
} 
 
#define N_STILL_FRAMES 1 
#define N_VIDEO_FRAMES 4 
 
static BOOL alloc_frame_list(frame_list_t *list) 
{ 
    int i; 
    int n; 
 
    if (list->format.is_still) 
        n = N_STILL_FRAMES; 
    else 
        n = N_VIDEO_FRAMES; 
 
    for (i = 0; i < n; i++) 
    { 
        frame_t* tmp = alloc_frame(list); 
        if (!tmp) 
        { 
            free_frame_list(list); 
            return FALSE; 
        } 
 
        frame_append(tmp); 
    } 
 
    return TRUE; 
} 
 
#define MAX_FRAM_LIST 20 
frame_list_t frame_list_tbl[MAX_FRAM_LIST]; 
UINT32 n_frame_list = 0; 
 
BOOL QCIDMAPrepareFormat(format_t* format) 
{ 
    frame_list_t *list = find_frame_list(format); 
 
    if (list) 
        return TRUE; 
     
    memset(&frame_list_tbl[n_frame_list], 0, sizeof(frame_list_t)); 
 
    memcpy(&frame_list_tbl[n_frame_list].format, format, sizeof(*format)); 
 
    alloc_frame_list(&frame_list_tbl[n_frame_list]); 
    if (frame_list_tbl[n_frame_list].head != NULL) 
    { 
        if (!video_frame_tail && !format->is_still) 
            video_frame_tail = frame_list_tbl[n_frame_list].tail; 
 
        n_frame_list++; 
        return TRUE; 
    } 
 
    memset(&frame_list_tbl[n_frame_list], 0, sizeof(frame_list_t)); 
    return FALSE; 
} 
 
static frame_list_t* find_frame_list(format_t* format) 
{ 
    UINT32 i; 
 
    for (i = 0; i < n_frame_list; i++) 
    { 
        frame_list_t* list = &frame_list_tbl[i]; 
 
        if (!list->head) 
            continue; 
 
        if (memcmp(&list->format, format, sizeof(format_t)) == 0) 
            return list; 
    } 
 
    return NULL; 
} 
 
BOOL QCIDMASetFrameFormat(format_t* format) 
{ 
    current_frame_list = find_frame_list(format);     
    return current_frame_list != NULL; 
} 
void DMALoad() 
 
{ 
    int i; 
    frame_t* f; 
 
    if (!current_frame_list) 
        return; 
 
    if (!current_frame_list->head) 
        return; 
 
    for (f = current_frame_list->head; f; f = f->next) 
    { 
        frame_mark(f); 
        frame_clean_cache(f); 
    } 
 
    for (i = 0; i < 3; i++) 
        PXA_CIDMALoadDescriptor(g_pCIRegs, 
                                current_frame_list->head->plane[i].desc_buf.phy_addr, 
                                PXA_CI_DMA_CHANNEL_0 + i); 
                                 
} 
 
#ifdef DEBUG 
 
static void dump_dma_chain(plane_t *plane) 
{ 
    UINT32 i; 
    PXA_CI_DMAC_DESCRIPTOR_T *descs; 
    UINT32 descs_n; 
 
    descs = (PXA_CI_DMAC_DESCRIPTOR_T*)plane->desc_buf.buf; 
    descs_n = plane->desc_buf.size / sizeof(*descs); 
 
    for (i = 0; i < descs_n; i++) 
    { 
        DEBUGMSG(ZONE_IOCTL,(L"%d ddadr 0x%08x, dsadr 0x%08x, dtadr 0x%08x, dcmd 0x%08x\r\n", 
            i, descs[i].ddadr, descs[i].dsadr, descs[i].dtadr, descs[i].dcmd));                 
    } 
} 
 
static void dump_frame(frame_t *frame) 
{ 
    int i; 
     
    short *buf; 
    DEBUGMSG(ZONE_IOCTL,(L"dumping frame 0x%08x\r\n",frame)); 
     
 
    if (!frame) 
        return; 
 
    for (i = 0; i < 3; i++) 
    { 
        DEBUGMSG(ZONE_IOCTL,(L"descriptor 0x%08x, phyiscal address 0x%08x\r\n", 
            frame->plane[i].desc_buf.buf, 
            frame->plane[i].desc_buf.phy_addr));         
        dump_dma_chain(&frame->plane[i]); 
    } 
 
    for (i = 0; i < 10; i++)         
        DEBUGMSG(ZONE_IOCTL,(L"0x%02x ",frame->plane[0].buf.buf[i])); 
             
    DEBUGMSG(ZONE_IOCTL,(L"dumping frame mark\r\n")); 
    buf = (short*)get_mark_ptr(frame); 
 
    for (i = 0; i < 4; i++) 
        DEBUGMSG(ZONE_IOCTL,(L"0x%04x \r\n",buf[i])); 
         
} 
 
void dump_frame_list(frame_list_t *frame_list) 
{ 
    frame_t *i; 
    DEBUGMSG(ZONE_IOCTL,(L"format %d\r\n", frame_list->format.format)); 
    DEBUGMSG(ZONE_IOCTL,(L"width %d\r\n", frame_list->format.width)); 
    DEBUGMSG(ZONE_IOCTL,(L"height %d\r\n", frame_list->format.height)); 
    DEBUGMSG(ZONE_IOCTL,(L"head 0x%08x\r\n", frame_list->head)); 
    DEBUGMSG(ZONE_IOCTL,(L"tail 0x%08x\r\n", frame_list->tail)); 
     
    for (i = frame_list->head; i; i = i->next) 
        dump_frame(i); 
} 
 
#endif 
 
int still_skips; 
 
DWORD WINAPI QciIntrThread() 
{ 
    int prio = 0; 
#define MAX_READY_FRAME 10 
    frame_t *frames[MAX_READY_FRAME]; 
    int n = 0; 
    int i; 
 
    prio = CeGetThreadPriority(GetCurrentThread()); 
 
    while(1)  
    { 
        int status; 
        //void qci_dump_regs(); 
        format_t* format; 
 
        status = WaitForSingleObject(qci_intr_event, INFINITE); 
        DEBUGMSG( ZONE_IOCTL, ( _T("CAM: Receive a complete frame!\r\n"))); 
        if (status == WAIT_FAILED)            
           DEBUGMSG( ZONE_IOCTL, ( _T("CAM: QciIntrThread: wait failed!\r\n")) ); 
         
        if (current_frame_list == NULL) 
        {             
            DEBUGMSG( ZONE_IOCTL, ( _T("CAM: Error no DMA frames!!!\r\n")) ); 
            continue; 
        } 
 
        format = ¤t_frame_list->format; 
        if (format->is_still) 
        { 
            if (still_skips) 
            { 
                DEBUGMSG( ZONE_IOCTL, ( _T("CAM: Still capture shoule skip %d frames!\r\n"),still_skips)); 
                still_skips--; 
            } else 
            {          
                DEBUGMSG( ZONE_IOCTL, ( _T("CAM: Receive a still frame!\r\n"))); 
                QciCallBack(current_frame_list->head); 
            } 
            PXA_CIClearInterruptStatus(g_pCIRegs, 0xFFFFFFFF); 
            InterruptDone(qci_sys_intr); 
            continue; 
        } 
 
        n = 0; 
        while (1)  
        { 
            frames[n] = frame_take(); 
 
            if (!frames[n]) 
                break; 
            n++; 
            if (n >= MAX_READY_FRAME) 
                break; 
        } 
 
        for (i = 0; i < n; i++) 
        { 
            QciCallBack(frames[i]); 
            frame_append(frames[i]); 
        } 
         
   
        PXA_CIClearInterruptStatus(g_pCIRegs, 0xFFFFFFFF); 
        InterruptDone(qci_sys_intr); 
    } 
 
 
    return TRUE; 
}