Ignore:
Timestamp:
Jul 17, 2012, 2:39:10 PM (12 years ago)
Author:
alain
Message:

Introducing support for FBDMA (Frame Buffer using DMA)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • soft/giet_vm/sys/drivers.c

    r167 r169  
    8585#define in_unckdata __attribute__((section (".unckdata")))
    8686
    87 in_unckdata volatile unsigned int  _dma_status[NB_DMAS];
    88 in_unckdata volatile unsigned char _dma_busy[NB_DMAS] = { [0 ... NB_DMAS-1] = 0 };
    89 
     87// IOC variables
    9088in_unckdata volatile unsigned char _ioc_status       = 0;
    9189in_unckdata volatile unsigned char _ioc_done         = 0;
    9290in_unckdata unsigned int                   _ioc_lock         = 0;
    9391in_unckdata unsigned int                   _ioc_iommu_ix1    = 0;
    94 in_unckdata unsigned int                   _ioc_iommu_npages = 0;
    95 
     92in_unckdata unsigned int                   _ioc_iommu_npages;
     93
     94// DMA variables
     95in_unckdata volatile unsigned int  _dma_status[NB_DMAS];
     96in_unckdata volatile unsigned char _dma_busy[NB_DMAS] = { [0 ... NB_DMAS-1] = 0 };
     97in_unckdata volatile unsigned char _dma_iommu_ix1     = 1;
     98in_unckdata volatile unsigned char _dma_iommu_npages[NB_DMAS];
     99
     100// TTY variables
    96101in_unckdata volatile unsigned char _tty_get_buf[NB_TTYS];
    97102in_unckdata volatile unsigned char _tty_get_full[NB_TTYS] = { [0 ... NB_TTYS-1] = 0 };
    98 in_unckdata unsigned int           _tty_put_lock;
     103in_unckdata unsigned int           _tty_put_lock          = 0;
    99104
    100105//////////////////////////////////////////////////////////////////////////////
     
    610615
    611616            // clear IOMMU TLB
    612             iob_address[IOB_INVAL_PTE] = (_ioc_iommu_ix1 << 21) | (ix2) << 12;
     617            iob_address[IOB_INVAL_PTE] = (_ioc_iommu_ix1 << 21) | (ix2 << 12);
    613618        }
    614619    }
     
    668673// implement the transfer between a data buffer (user space) and the frame
    669674// buffer (kernel space). They are blocking until completion of the transfer.
     675//
    670676// The '_fb_write()', '_fb_read()' and '_fb_completed()' functions use the DMA
    671677// coprocessor to transfer data between the user buffer and the frame buffer.
    672678// These  functions use a polling policy to test the global variables _dma_busy[i]
    673679// and detect the transfer completion. 
    674 // There is  NB_PROCS DMA channels, that are indexed by the proc_id.
     680// There is  NB_DMA channels, that are indexed by the dma_id stored in the
     681// task context.
    675682// The _dma_busy[i] synchronisation variables (one per channel) are set by the OS,
    676 // and reset by the ISR.
     683// and reset by the DMA ISR.
    677684//////////////////////////////////////////////////////////////////////////////////
    678685
     
    692699    volatile unsigned char *fb_address;
    693700
    694     /* buffer must be in user space */
    695     if (((unsigned int)buffer >= 0x80000000)
    696             || (((unsigned int)buffer + length ) >= 0x80000000 ))
     701    // buffer must be mapped in user space
     702    if ( ((unsigned int)buffer + length ) >= 0x80000000 )
    697703        return 1;
    698704
    699705    fb_address = (unsigned char*)&seg_fb_base + offset;
    700706
    701     /* buffer copy */
     707    // buffer copy
    702708    memcpy((void*)fb_address, (void*)buffer, length);
    703709
     
    720726    volatile unsigned char *fb_address;
    721727
    722     /* parameters checking */
    723     /* buffer must be in user space */
    724     if (((unsigned int)buffer >= 0x80000000)
    725             || (((unsigned int)buffer + length ) >= 0x80000000 ))
     728    // buffer must be mapped in user space
     729    if ( ((unsigned int)buffer + length ) >= 0x80000000 )
    726730        return 1;
    727731
    728732    fb_address = (unsigned char*)&seg_fb_base + offset;
    729733
    730     /* buffer copy */
     734    // buffer copy
    731735    memcpy((void*)buffer, (void*)fb_address, length);
    732736
     
    735739
    736740//////////////////////////////////////////////////////////////////////////////////
     741// _fb_access()
     742// Transfer data between a memory buffer and the frame_buffer device using DMA.
     743// - to_mem     : from frame buffer to memory when true.
     744// - offset     : offset (in bytes) in the frame buffer.
     745// - user_vaddr : virtual base address of the memory buffer.
     746// - length     : number of bytes to be transfered.
     747// The memory buffer must be mapped in user address space and word-aligned.
     748// The user buffer length must be multiple of 4 bytes.
     749// Returns 0 if success, > 0 if error.
     750//////////////////////////////////////////////////////////////////////////////////
     751unsigned int _fb_access( unsigned int   to_mem,
     752                         unsigned int   offset,
     753                         unsigned int   user_vaddr,
     754                         unsigned int   length )
     755{
     756    static_scheduler_t* psched;         // pointer on the current task scheduler
     757    unsigned char*              fb_base;        // frame buffer base address
     758    unsigned int*               dma_base;       // dma component base address
     759    unsigned int                task_id;        // task local index (for scheduler)
     760    unsigned int                dma_id;         // DMA channel index
     761    unsigned int                vpn;            // current virtual page number
     762    unsigned int                flags;          // protection flags
     763    unsigned int                ppn;            // current physical page number
     764    unsigned int                buf_base;       // buffer base address for DMA access
     765    unsigned int                ppn_first;      // first physical page index for user buffer
     766
     767    fb_base  = (unsigned char*)&seg_fb_base + offset;
     768
     769    psched   = &_scheduler[_procid()];
     770    task_id  = psched->current;
     771    dma_id   = psched->context[task_id][CTX_FBDMA_ID];
     772    dma_base = (unsigned int*)&seg_dma_base + (dma_id * DMA_SPAN);
     773
     774    // check buffer address and ength alignment
     775    if ( user_vaddr & 0x3 ) return 1;
     776    if ( length     & 0x3 ) return 1;
     777
     778    // get user space page table virtual address
     779    unsigned int user_ptp = psched->context[task_id][CTX_PTAB_ID];
     780
     781    unsigned int user_vpn_min = user_vaddr >> 12;
     782    unsigned int user_vpn_max = (user_vaddr + length - 1) >> 12;
     783    unsigned int ix2          = 0;
     784    unsigned int ix1          = _dma_iommu_ix1 + dma_id;
     785    unsigned int ko;
     786    unsigned int i;
     787
     788    // loop on all virtual pages covering the user buffer
     789    for ( vpn = user_vpn_min ; vpn <= user_vpn_max ; vpn++ )
     790    {
     791        // get ppn and flags for each vpn
     792        ko = _v2p_translate( (page_table_t*)user_ptp,
     793                             vpn,
     794                             &ppn,
     795                             &flags );
     796
     797        // check access rights
     798        if ( ko )                                 return 2;     // unmapped
     799        if ( (flags & PTE_U) == 0 )               return 3;     // not in user space
     800        if ( ( (flags & PTE_W) == 0 ) && to_mem ) return 4;     // not writable
     801
     802        // save first ppn value
     803        if ( ix2 == 0 ) ppn_first = ppn;
     804
     805        if ( GIET_IOMMU_ACTIVE )    // the user buffer must be remapped in the I/0 space
     806        {
     807            // check buffer length < 2 Mbytes
     808            if ( ix2 > 511 ) return 2;
     809
     810            // map the physical page in IOMMU page table
     811            _iommu_add_pte2( ix1,               // PT1 index
     812                             ix2,               // PT2 index
     813                             ppn,               // physical page number
     814                             flags );   // protection flags
     815        }
     816        else            // no IOMMU : check that physical pages are contiguous
     817        {
     818            if ( (ppn - ppn_first) != ix2 )       return 5;     // split physical buffer 
     819        }
     820
     821        // increment page index
     822        ix2++;
     823    } // end for vpn
     824
     825    // register the number of pages to be unmapped
     826    _dma_iommu_npages[dma_id] = (user_vpn_max - user_vpn_min) + 1;
     827
     828    // invalidate data cache in case of memory write
     829    if ( to_mem ) _dcache_buf_invalidate( (void*)user_vaddr, length );
     830
     831    // compute buffer base address for DMA depending on IOMMU activation
     832    if ( GIET_IOMMU_ACTIVE ) buf_base = ( ix1 ) << 21 | (user_vaddr & 0xFFF);
     833    else                     buf_base = (ppn_first << 12) | (user_vaddr & 0xFFF);
     834
     835
     836    // waiting until DMA device is available
     837    while (_dma_busy[dma_id] != 0)
     838    {
     839        // busy wait with a pseudo random delay between bus access
     840        unsigned int delay = (_proctime() & 0xF) << 4;
     841        for (i = 0; i < delay; i++)
     842            asm volatile("nop");
     843    }
     844
     845    _dma_busy[dma_id] = 1;
     846
     847    // DMA configuration
     848    dma_base[DMA_IRQ_DISABLE] = 0;
     849    if ( to_mem )
     850    {
     851        dma_base[DMA_SRC] = (unsigned int)fb_base;
     852        dma_base[DMA_DST] = (unsigned int)buf_base;
     853    }
     854    else
     855    {
     856        dma_base[DMA_SRC] = (unsigned int)buf_base;
     857        dma_base[DMA_DST] = (unsigned int)fb_base;
     858    }
     859    dma_base[DMA_LEN] = (unsigned int)length;
     860   
     861    return 0;
     862
     863//////////////////////////////////////////////////////////////////////////////////
    737864// _fb_write()
    738 // Transfer data from an memory buffer to the frame_buffer device using a DMA.
    739 // The source memory buffer must be in user address space.
     865// Transfer data from a memory buffer to the frame_buffer device using  DMA.
    740866// - offset : offset (in bytes) in the frame buffer.
    741867// - buffer : base address of the memory buffer.
     
    744870//////////////////////////////////////////////////////////////////////////////////
    745871unsigned int _fb_write( unsigned int    offset,
    746                         const void*     buffer,
     872                        const void*             buffer,
    747873                        unsigned int    length )
    748874{
    749     volatile unsigned char *fb_address;
    750     volatile unsigned int *dma;
    751 
    752     unsigned int proc_id;
    753     unsigned int delay;
    754     unsigned int i;
    755 
    756     /* buffer must be in user space */
    757     if (((unsigned int)buffer >= 0x80000000)
    758             || (((unsigned int)buffer + length ) >= 0x80000000 ))
    759         return 1;
    760 
    761     proc_id = _procid();
    762     fb_address = (unsigned char*)&seg_fb_base + offset;
    763     dma = (unsigned int*)&seg_dma_base + (proc_id * DMA_SPAN);
    764 
    765     /* waiting until DMA device is available */
    766     while (_dma_busy[proc_id] != 0)
    767     {
    768         /* if the lock failed, busy wait with a pseudo random delay between bus
    769          * accesses */
    770         delay = (_proctime() & 0xF) << 4;
     875    return _fb_access( 0,                                               // write to frame buffer
     876                       offset,
     877                       (unsigned int)buffer,
     878                       length );       
     879}
     880
     881//////////////////////////////////////////////////////////////////////////////////
     882// _fb_read()
     883// Transfer data from the frame_buffer device to a memory buffer using  DMA.
     884// - offset : offset (in bytes) in the frame buffer.
     885// - buffer : base address of the memory buffer.
     886// - length : number of bytes to be transfered.
     887// Returns 0 if success, > 0 if error.
     888//////////////////////////////////////////////////////////////////////////////////
     889unsigned int _fb_read( unsigned int     offset,
     890                       const void*              buffer,
     891                       unsigned int     length )
     892{
     893    return _fb_access( 1,                                               // read from frame buffer
     894                       offset,
     895                       (unsigned int)buffer,
     896                       length );       
     897}
     898
     899//////////////////////////////////////////////////////////////////////////////////
     900// _fb_completed()
     901// This function checks completion of a DMA transfer to or fom the frame buffer.
     902// As it is a blocking call, the processor is busy waiting.
     903// Returns 0 if success, > 0 if error
     904// (1 == read error / 2 == DMA idle error / 3 == write error)
     905//////////////////////////////////////////////////////////////////////////////////
     906unsigned int _fb_completed()
     907{
     908    static_scheduler_t* psched  = &_scheduler[_procid()];
     909    unsigned int        task_id = psched->current;
     910
     911    volatile unsigned int   dma_id  = psched->context[task_id][CTX_FBDMA_ID];
     912
     913    // busy waiting with a pseudo random delay between bus access
     914    while (_dma_busy[dma_id] != 0)
     915    {
     916            unsigned int i;
     917        unsigned int delay = (_proctime() & 0xF) << 4;
    771918        for (i = 0; i < delay; i++)
    772919            asm volatile("nop");
    773920    }
    774     _dma_busy[proc_id] = 1;
    775 
    776     /* DMA configuration for write transfer */
    777     dma[DMA_IRQ_DISABLE] = 0;
    778     dma[DMA_SRC] = (unsigned int)buffer;
    779     dma[DMA_DST] = (unsigned int)fb_address;
    780     dma[DMA_LEN] = (unsigned int)length;
    781     return 0;
    782 }
    783 
    784 //////////////////////////////////////////////////////////////////////////////////
    785 // _fb_read()
    786 // Transfer data from the frame_buffer device to an memory buffer using a DMA.
    787 // The destination memory buffer must be in user address space.
    788 // - offset : offset (in bytes) in the frame buffer.
    789 // - buffer : base address of the memory buffer.
    790 // - length : number of bytes to be transfered.
    791 // All cache lines corresponding to the the target buffer are invalidated
    792 // for cache coherence.
    793 // Returns 0 if success, > 0 if error.
    794 //////////////////////////////////////////////////////////////////////////////////
    795 unsigned int _fb_read( unsigned int     offset,
    796                        const void*      buffer,
    797                        unsigned int     length )
    798 {
    799     volatile unsigned char *fb_address;
    800     volatile unsigned int *dma;
    801 
    802     unsigned int proc_id;
    803     unsigned int delay;
    804     unsigned int i;
    805 
    806     /* buffer must be in user space */
    807     if (((unsigned int)buffer >= 0x80000000)
    808             || (((unsigned int)buffer + length ) >= 0x80000000 ))
    809         return 1;
    810 
    811     proc_id = _procid();
    812     fb_address = (unsigned char*)&seg_fb_base + offset;
    813     dma = (unsigned int*)&seg_dma_base + (proc_id * DMA_SPAN);
    814 
    815     /* waiting until DMA device is available */
    816     while (_dma_busy[proc_id] != 0)
    817     {
    818         /* if the lock failed, busy wait with a pseudo random delay between bus
    819          * accesses */
    820         delay = (_proctime() & 0xF) << 4;
    821         for (i = 0; i < delay; i++)
    822             asm volatile("nop");
     921   
     922    // unmap the buffer from IOMMU page table if IOMMU is activated
     923    if ( GIET_IOMMU_ACTIVE )
     924    {
     925        unsigned int* iob_address = (unsigned int*)&seg_iob_base;
     926        unsigned int  ix1         = _dma_iommu_ix1 + dma_id;
     927        unsigned int  ix2;
     928
     929        for ( ix2 = 0 ; ix2 < _dma_iommu_npages[dma_id] ; ix2++ )
     930        {
     931            // unmap the page in IOMMU page table
     932            _iommu_inval_pte2( ix1,             // PT1 index
     933                               ix2 );   // PT2 index
     934
     935            // clear IOMMU TLB
     936            iob_address[IOB_INVAL_PTE] = (ix1 << 21) | (ix2 << 12);
     937        }
    823938    }
    824     _dma_busy[proc_id] = 1;
    825 
    826     /* DMA configuration for write transfer */
    827     dma[DMA_IRQ_DISABLE] = 0;
    828     dma[DMA_SRC] = (unsigned int)fb_address;
    829     dma[DMA_DST] = (unsigned int)buffer;
    830     dma[DMA_LEN] = (unsigned int)length;
    831 
    832     /* invalidation of data cache */
    833     _dcache_buf_invalidate(buffer, length);
    834 
    835     return 0;
    836 }
    837 
    838 //////////////////////////////////////////////////////////////////////////////////
    839 // _fb_completed()
    840 // This function checks completion of a DMA transfer to or fom the frame buffer.
    841 // As it is a blocking call, the processor is stalled until the next interrupt.
    842 // Returns 0 if success, > 0 if error.
    843 //////////////////////////////////////////////////////////////////////////////////
    844 unsigned int _fb_completed()
    845 {
    846     unsigned int proc_id;
    847 
    848     proc_id = _procid();
    849 
    850     while (_dma_busy[proc_id] != 0)
    851         asm volatile("nop");
    852 
    853     if (_dma_status[proc_id] != 0)
    854         return 1;
    855 
    856     return 0;
    857 }
    858 
     939
     940    return _dma_status[dma_id];
     941}
     942
Note: See TracChangeset for help on using the changeset viewer.