/////////////////////////////////////////////////////////////////////////////////// // 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; // array of FB_CMA channels descriptors (32 bytes per entry) // each entry contains one SRC and one DST chbuf descriptors. in_unckdata volatile fb_cma_channel_t _fb_cma_channel[NB_CMA_CHANNELS] __attribute__((aligned(64))); // array of physical addresses for the FB_CMA channels descriptors 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), 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 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 desc_paddr; // 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; } _fb_cma_desc_paddr[channel_id] = (((paddr_t)ppn) << 12) | (vaddr & 0x00000FFF); desc_paddr = (((paddr_t)ppn) << 12) | (vaddr & 0x00000FFF); #if GIET_DEBUG_FBF_DRIVER _tty_get_lock( 0 ); _puts("\n[CMA DEBUG] fb_cma_init()"); _puts("\n - fbf pbase = "); _putl( _fb_cma_channel[channel_id].fbf ); _puts("\n - buf0 pbase = "); _putl( _fb_cma_channel[channel_id].buf0 ); _puts("\n - buf1 pbase = "); _putl( _fb_cma_channel[channel_id].buf1 ); _puts("\n - channel pbase = "); _putl( _fb_cma_desc_paddr[channel_id] ); _puts("\n"); _tty_release_lock( 0 ); #endif // SYNC request for channel descriptor _memc_sync( desc_paddr, 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)(desc_paddr & 0xFFFFFFFF); cma_vbase[offset + CHBUF_SRC_EXT] = (unsigned int)(desc_paddr >> 32); cma_vbase[offset + CHBUF_SRC_NBUFS] = 2; cma_vbase[offset + CHBUF_DST_DESC] = (unsigned int)(desc_paddr & 0xFFFFFFFF) + 16; cma_vbase[offset + CHBUF_DST_EXT] = (unsigned int)(desc_paddr >> 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() // // It updates the status of the SRC and DST chbuf descriptors, to allow the CMA // component to transfer the source user buffer to the frame buffer. // // If the IO Bridge component is used: // 1) it makes an INVAL request for the channel descriptor, before testing the // source buffer status, because it is modified in XRAM by the CMA component. // 2) it makes a SYNC request for the source user buffer before activating the CMA // transfer, because the data will be read from XRAM by the CMA component. // 3) it makes a SYNC request for the channel descriptor after modification // of the SRC and DST status, because these descriptors will be read from XRAM // by the CMA component. // // The buffer_id argument is the 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 volatile paddr_t buf_paddr; unsigned int buf_length; unsigned int full = 1; unsigned int count = 0; // get CMA channel index unsigned int channel_id = _get_context_slot(CTX_CMA_ID); #if GIET_DEBUG_FBF_DRIVER _tty_get_lock( 0 ); _puts("\n[CMA DEBUG] fb_cma_write() for CMA channel "); _putd( channel_id ); _puts(" / buf_id = "); _putd( buffer_id ); _puts("\n"); _tty_release_lock( 0 ); #endif // waiting buffer empty while ( full ) { if ( USE_IOB ) { // INVAL L2 cache for the channel descriptor, _memc_inval( _fb_cma_desc_paddr[channel_id], 32 ); // INVAL L1 cache for the channel descriptor, _dcache_buf_invalidate( &_fb_cma_channel[channel_id], 32 ); } // read SRC buffer descriptor if ( buffer_id == 0 ) buf_paddr = _fb_cma_channel[channel_id].buf0; else buf_paddr = _fb_cma_channel[channel_id].buf1; full = ( (unsigned int)(buf_paddr>>63) ); count++; if ( count == 10 ) _exit(); #if GIET_DEBUG_FBF_DRIVER _tty_get_lock( 0 ); _puts(" - buffer descriptor = "); _putl( buf_paddr ); _puts(" at cycle "); _putd( _get_proctime() ); _puts("\n"); _tty_release_lock( 0 ); #endif } if ( USE_IOB ) { // SYNC request for the user buffer because // this buffer will be read from XRAM by the CMA component _memc_sync( buf_paddr, _fb_cma_channel[channel_id].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; if ( USE_IOB ) { // SYNC request for the channel descriptor, because // it will be read in XRAM by the CMA component _memc_sync( _fb_cma_desc_paddr[channel_id], 32 ); } 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