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; }