/////////////////////////////////////////////////////////////////////////////////// // File : fbf_driver.c // Date : 23/05/2013 // Author : alain greiner // Copyright (c) UPMC-LIP6 /////////////////////////////////////////////////////////////////////////////////// // The fbf_driver.c and fbf_driver.h files are part ot the GIET-VM kernel. // This driver supports the SoCLib vci_framebuffer component. // // It can exist only one frame buffer in the architecture. // // There exist two methods to access the VciFrameBuffer device: // // 1) The _fb_sync_write() and _fb_sync_read() functions use a memcpy strategy // to implement the transfer between a data buffer (user space) and the frame // buffer (kernel space). They are blocking until completion of the transfer. // // 2) The _fb_cma_init(), _fb_cma_write() and _fb_cma_stop() functions use // the VciChbufDma component (non replicated) to transfer a flow of images from // an user space chained buffer (two buffers) to the frame buffer. // A CMA channel must be allocated to the task requesting it in the mapping_info, // and stored in the task context. /////////////////////////////////////////////////////////////////////////////////// // The seg_fbf_base virtual address must be defined in giet_vsegs.ld file. /////////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #if !defined(GIET_USE_IOMMU) # error: You must define GIET_USE_IOMMU in the giet_config.h file #endif #if !defined( USE_IOB ) # error: You must define USE_IOB in the hard_config.h file #endif #define in_unckdata __attribute__((section (".unckdata"))) ////////////// memcpy approach ////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////// // _fb_sync_write() // Transfer data from an memory buffer to the frame_buffer device using a memcpy. // - offset : offset (in bytes) in the frame buffer. // - buffer : base address of the memory buffer. // - length : number of bytes to be transfered. ////////////////////////////////////////////////////////////////////////////////// unsigned int _fb_sync_write(unsigned int offset, const void * buffer, unsigned int length) { char* fb_address = (char *)&seg_fbf_base + offset; _memcpy( fb_address, buffer, length); return 0; } ////////////////////////////////////////////////////////////////////////////////// // _fb_sync_read() // Transfer data from the frame_buffer device to a memory buffer using a memcpy. // - offset : offset (in bytes) in the frame buffer. // - buffer : base address of the memory buffer. // - length : number of bytes to be transfered. ////////////////////////////////////////////////////////////////////////////////// unsigned int _fb_sync_read( unsigned int offset, void* buffer, unsigned int length) { char* fb_address = (char *)&seg_fbf_base + offset; _memcpy( buffer, fb_address, length); return 0; } ///////////////// CDMA approach ////////////////////////////////////////////////// // This structure contains two chbuf descriptors that can be used by // the VciChbufDma component to tranfer a flow of images: // - The SRC chbuf descriptor contain two slots (two user buffers) // - The DST chbuf descriptor contains only one slot (frame buffer) typedef struct fb_cma_channel_s { paddr_t buf0; // physical address + status for user buffer 0 paddr_t buf1; // physical address + status for user buffer 1 paddr_t fbf; // physical address + status for frame buffer unsigned int length; // buffer length (number of bytes) unsigned int padding; // unused (just to have channel size = 32 bytes) } fb_cma_channel_t; in_unckdata volatile fb_cma_channel_t _fb_cma_channel[NB_CMA_CHANNELS] __attribute__((aligned(64))); in_unckdata volatile paddr_t _fb_cma_desc_paddr[NB_CMA_CHANNELS]; ////////////////////////////////////////////////////////////////////////////////////// // _fb_cma_init() // This function uses the _fb_cma_channel[] and _fb_cma_desc_paddr[] arrays, // that are both indexed by the channel index. // where each entry contains one fb_cma_channel structure (defining two // SRC and DST chbuf descriptors), and does four things: // // 1) computes the physical addresses for the two source user buffers, for // the destination frame buffer. It initialises the channel descriptor // _fb_cma_channel[i], containing the SRC chbuf descriptor (two buffers), // the DST chbuf descriptor (one single frame buffer), and the buffer length. // // 2) computes the physical address for the channel descriptor and register it // in the _fb_cma_desc_paddr[i]. // // 3) makes a SYNC request to L2 cache for channel descriptor, because the // channel descriptor is directly accessed in XRAM by the CMA component. // // 4) Starts the CMA hardware channel, that will poll the channel descriptor // to fransfer an user buffer to the frame buffer as soon as the source // user buffer is marked valid. // // Returns 0 if success, > 0 if error ////////////////////////////////////////////////////////////////////////////////////// unsigned int _fb_cma_init( const void* vbase0, // first user buffer vbase address const void* vbase1, // second user buffer vbase address unsigned int length ) // buffer length (number of bytes) { #if NB_CMA_CHANNELS > 0 unsigned int channel_id; // CMA channel index unsigned int user_ptab; // page table virtual address unsigned int ko; // unsuccessfull V2P translation unsigned int vaddr; // virtual address unsigned int flags; // protection flags unsigned int ppn; // physical page number paddr_t channel_pbase; // physical address of channel descriptor // get CMA channel index channel_id = _get_context_slot(CTX_CMA_ID); if ( channel_id >= NB_CMA_CHANNELS ) { _tty_get_lock( 0 ); _puts("\n[GIET ERROR] in _fb_cma_init() : CMA channel index too large\n"); _tty_release_lock( 0 ); return 1; } // checking size for channel descriptor if ( sizeof(fb_cma_channel_t) != 32 ) { _tty_get_lock( 0 ); _puts("\n[GIET ERROR] in _fb_cma_init() : bad fb_cma_channel size\n"); _tty_release_lock( 0 ); return 1; } // checking channel descriptor alignment (32 bytes) if ( (unsigned int)(&_fb_cma_channel[channel_id]) & 0x1F ) { _tty_get_lock( 0 ); _puts("\n[GIET ERROR] in _fb_cma_init() : bad fb_cma_channel alignment\n"); _tty_release_lock( 0 ); return 1; } // checking user buffer virtual addresses and length alignment if ( ((unsigned int)vbase0 & 0x3) || ((unsigned int)vbase1 & 0x3) || (length & 0x3) ) { _tty_get_lock( 0 ); _puts("\n[GIET ERROR] in _fb_cma_init() : user buffer not word aligned\n"); _tty_release_lock( 0 ); return 1; } // get page table virtual address user_ptab = _get_context_slot(CTX_PTAB_ID); // compute and register frame buffer physical address vaddr = ((unsigned int)&seg_fbf_base); ko = _v2p_translate( (page_table_t*) user_ptab, (vaddr >> 12), &ppn, &flags ); if (ko) { _tty_get_lock( 0 ); _puts("\n[GIET ERROR] in _fb_cma_init() : frame buffer unmapped\n"); _tty_release_lock( 0 ); return 1; } _fb_cma_channel[channel_id].fbf = ((paddr_t)ppn << 12) | (vaddr & 0x00000FFF); // Compute and register first user buffer physical address vaddr = (unsigned int)vbase0; ko = _v2p_translate( (page_table_t*) user_ptab, (vaddr >> 12), &ppn, &flags ); if (ko) { _tty_get_lock( 0 ); _puts("\n[GIET ERROR] in _fb_cma_init() : user buffer 0 unmapped\n"); _tty_release_lock( 0 ); return 1; } if ((flags & PTE_U) == 0) { _tty_get_lock( 0 ); _puts("[GIET ERROR] in _fb_cma_init() : user buffer 0 not in user space\n"); _tty_release_lock( 0 ); return 1; } _fb_cma_channel[channel_id].buf0 = ((paddr_t)ppn << 12) | (vaddr & 0x00000FFF); // Compute and register second user buffer physical address vaddr = (unsigned int)vbase1; ko = _v2p_translate( (page_table_t*) user_ptab, (vaddr >> 12), &ppn, &flags ); if (ko) { _tty_get_lock( 0 ); _puts("\n[GIET ERROR] in _fb_cma_init() : user buffer 1 unmapped\n"); _tty_release_lock( 0 ); return 1; } if ((flags & PTE_U) == 0) { _tty_get_lock( 0 ); _puts("[GIET ERROR] in _fb_cma_init() : user buffer 1 not in user space\n"); _tty_release_lock( 0 ); return 1; } _fb_cma_channel[channel_id].buf1 = ((paddr_t)ppn << 12) | (vaddr & 0x00000FFF); // register buffer length in channel descriptor _fb_cma_channel[channel_id].length = length; // Compute and register physical adress of the channel descriptor vaddr = (unsigned int)(&_fb_cma_channel[channel_id]); ko = _v2p_translate( (page_table_t*) user_ptab, (vaddr >> 12), &ppn, &flags ); if (ko) { _tty_get_lock( 0 ); _puts("\n[GIET ERROR] in _fb_cma_init() : channel descriptor unmapped\n"); _tty_release_lock( 0 ); return 1; } channel_pbase = (((paddr_t)ppn) << 12) | (vaddr & 0x00000FFF); _fb_cma_desc_paddr[channel_id] = channel_pbase; #if GIET_DEBUG_CMA_DRIVER _puts("\n"); _puts("- fbf pbase = "); _putl( _fb_cma_channel[channel_id].fbf ); _puts("\n"); _puts("- buf0 pbase = "); _putl( _fb_cma_channel[channel_id].buf0 ); _puts("\n"); _puts("- buf1 pbase = "); _putl( _fb_cma_channel[channel_id].buf1 ); _puts("\n"); _puts("- channel pbase = "); _putl( channel_pbase ); _puts("\n"); #endif // SYNC request for channel descriptor _memc_sync( channel_pbase, 32 ); // CMA channel activation unsigned int* cma_vbase = (unsigned int *)&seg_cma_base; unsigned int offset = channel_id * CHBUF_CHANNEL_SPAN; cma_vbase[offset + CHBUF_SRC_DESC] = (unsigned int)(channel_pbase & 0xFFFFFFFF); cma_vbase[offset + CHBUF_SRC_EXT] = (unsigned int)(channel_pbase >> 32); cma_vbase[offset + CHBUF_SRC_NBUFS] = 2; cma_vbase[offset + CHBUF_DST_DESC] = (unsigned int)(channel_pbase & 0xFFFFFFFF) + 16; cma_vbase[offset + CHBUF_DST_EXT] = (unsigned int)(channel_pbase >> 32); cma_vbase[offset + CHBUF_DST_NBUFS] = 1; cma_vbase[offset + CHBUF_BUF_SIZE] = length; cma_vbase[offset + CHBUF_PERIOD] = 300; cma_vbase[offset + CHBUF_RUN] = 1; return 0; #else _tty_get_lock( 0 ); _puts("\n[GIET ERROR] in _fb_cma_init() : no CMA channel allocated\n"); _tty_release_lock( 0 ); return 1; #endif } ////////////////////////////////////////////////////////////////////////////////// // _fb_cma_write() // This function makes a SYNC request for the source user buffer. // Then it updates the status of the SRC and DST chbuf descriptors, to allow // the CMA component to transfer the source user buffer buffer to the destination // frame buffer, and makes a SYNC request for the channel descriptor. // // - buffer_id : user buffer index (0 => buf0 / not 0 => buf1) // Returns 0 if success, > 0 if error ////////////////////////////////////////////////////////////////////////////////// unsigned int _fb_cma_write( unsigned int buffer_id ) { #if NB_CMA_CHANNELS > 0 paddr_t buf_paddr; unsigned int buf_length; // get CMA channel index unsigned int channel_id = _get_context_slot(CTX_CMA_ID); // SYNC request for the source user buffer if ( buffer_id == 0 ) buf_paddr = _fb_cma_channel[channel_id].buf0; else buf_paddr = _fb_cma_channel[channel_id].buf1; buf_length = _fb_cma_channel[channel_id].length; _memc_sync( buf_paddr, buf_length ); // set SRC full if ( buffer_id == 0 ) _fb_cma_channel[channel_id].buf0 = buf_paddr | 0x8000000000000000ULL; else _fb_cma_channel[channel_id].buf1 = buf_paddr | 0x8000000000000000ULL; // set DST empty _fb_cma_channel[channel_id].fbf = _fb_cma_channel[channel_id].fbf & 0x7FFFFFFFFFFFFFFFULL; // SYNC request for the channel descriptor buf_paddr = _fb_cma_desc_paddr[channel_id]; buf_length = 32; _memc_sync( buf_paddr, buf_length ); return 0; #else _tty_get_lock( 0 ); _puts("\n[GIET ERROR] in _fb_cma_channel() : no CMA channel allocated\n"); _tty_release_lock( 0 ); return 1; #endif } ////////////////////////////////////////////////////////////////////////////////// // _fb_cma_stop() // This function desactivates the CMA channel allocated to the calling task. // Returns 0 if success, > 0 if error ////////////////////////////////////////////////////////////////////////////////// unsigned int _fb_cma_stop( unsigned int buffer_id ) { #if NB_CMA_CHANNELS > 0 // get CMA channel allocated unsigned int channel_id = _get_context_slot(CTX_CMA_ID); // CMA channel desactivation unsigned int* cma_vbase = (unsigned int *)&seg_cma_base; unsigned int offset = channel_id * CHBUF_CHANNEL_SPAN; cma_vbase[offset + CHBUF_RUN] = 0; return 0; #else _tty_get_lock( 0 ); _puts("\n[GIET ERROR] in _fb_cma_stop() : no CMA channel allocated\n"); _tty_release_lock( 0 ); return 1; #endif } // Local Variables: // tab-width: 4 // c-basic-offset: 4 // c-file-offsets:((innamespace . 0)(inline-open . 0)) // indent-tabs-mode: nil // End: // vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4