F5OEO-librpitx/src/dma.cpp

307 wiersze
8.1 KiB
C++

/*
Copyright (C) 2018 Evariste COURJAUD F5OEO
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dma.h"
#include "stdio.h"
#include "util.h"
#include "rpi.h"
extern "C"
{
#include "mailbox.h"
#include "raspberry_pi_revision.h"
}
#include <unistd.h>
#define BUS_TO_PHYS(x) ((x)&~0xC0000000)
dma::dma(int Channel,uint32_t CBSize,uint32_t UserMemSize) // Fixme! Need to check to be 256 Aligned for UserMem
{
//Channel DMA is now hardcoded according to Raspi Model (DMA 7 for Pi4, DMA 14 for others)
uint32_t BCM2708_PERI_BASE =bcm_host_get_peripheral_address();
channel=Channel;
if(BCM2708_PERI_BASE==0xFE000000)
{
channel= 7; // Pi4
dbg_printf(1,"dma PI4 using channel %d\n",channel);
}
else
{
channel = 14; // Other Pi
dbg_printf(1,"dma (NOT a PI4) using channel %d\n",channel);
}
dbg_printf(1,"channel %d CBSize %u UsermemSize %u\n",channel,CBSize,UserMemSize);
mbox.handle = mbox_open();
if (mbox.handle < 0)
{
dbg_printf(1,"Failed to open mailbox\n");
}
cbsize=CBSize;
usermemsize=UserMemSize;
GetRpiInfo(); // Fill mem_flag and dram_phys_base
uint32_t MemoryRequired=CBSize*sizeof(dma_cb_t)+UserMemSize*sizeof(uint32_t);
int NumPages=(MemoryRequired/PAGE_SIZE)+1;
dbg_printf(1,"%d Size NUM PAGES %d PAGE_SIZE %d\n",MemoryRequired,NumPages,PAGE_SIZE);
mbox.mem_ref = mem_alloc(mbox.handle, NumPages* PAGE_SIZE, PAGE_SIZE, mem_flag);
/* TODO: How do we know that succeeded? */
//dbg_printf(1,"mem_ref %x\n", mbox.mem_ref);
mbox.bus_addr = mem_lock(mbox.handle, mbox.mem_ref);
//dbg_printf(1,"bus_addr = %x\n", mbox.bus_addr);
mbox.virt_addr = (uint8_t *)mapmem(BUS_TO_PHYS(mbox.bus_addr), NumPages* PAGE_SIZE);
//dbg_printf(1,"virt_addr %p\n", mbox.virt_addr);
virtbase = (uint8_t *)((uint32_t *)mbox.virt_addr);
//dbg_printf(1,"virtbase %p\n", virtbase);
cbarray = (dma_cb_t *)virtbase; // We place DMA Control Blocks (CB) at beginning of virtual memory
//dbg_printf(1,"cbarray %p\n", cbarray);
usermem= (unsigned int *)(virtbase+CBSize*sizeof(dma_cb_t)); // user memory is placed after
//dbg_printf(1,"usermem %p\n", usermem);
dma_reg.gpioreg[DMA_CS+channel*0x40] = BCM2708_DMA_RESET|DMA_CS_INT; // Remove int flag
usleep(100);
dma_reg.gpioreg[DMA_CONBLK_AD+channel*0x40]=mem_virt_to_phys((void*)cbarray ); // reset to beginning
//get_clocks(mbox.handle);
}
void dma::GetRpiInfo()
{
dram_phys_base= bcm_host_get_sdram_address();
mem_flag = MEM_FLAG_HINT_PERMALOCK|MEM_FLAG_NO_INIT;//0x0c;
switch(dram_phys_base)
{
case 0x40000000 : mem_flag |=MEM_FLAG_L1_NONALLOCATING;break;
case 0xC0000000 : mem_flag |=MEM_FLAG_DIRECT;break;
default: dbg_printf(0,"Unknown Raspberry architecture\n");
}
}
dma::~dma()
{
stop();
mem_unlock(mbox.handle, mbox.mem_ref);
mem_free(mbox.handle, mbox.mem_ref);
}
uint32_t dma::mem_virt_to_phys(volatile void *virt)
{
//MBOX METHOD
uint32_t offset = (uint8_t *)virt - mbox.virt_addr;
return mbox.bus_addr + offset;
}
uint32_t dma::mem_phys_to_virt(volatile uint32_t phys)
{
//MBOX METHOD
uint32_t offset=phys-mbox.bus_addr;
uint32_t result=(size_t)((uint8_t *)mbox.virt_addr+offset);
//printf("MemtoVirt:Offset=%lx phys=%lx -> %lx\n",offset,phys,result);
return result;
}
int dma::start()
{
dma_reg.gpioreg[DMA_CS+channel*0x40] = BCM2708_DMA_RESET;
usleep(100);
dma_reg.gpioreg[DMA_CONBLK_AD+channel*0x40]=mem_virt_to_phys((void*)cbarray ); // reset to beginning
dma_reg.gpioreg[DMA_DEBUG+channel*0x40] = 7; // clear debug error flags
usleep(100);
dma_reg.gpioreg[DMA_CS+channel*0x40] = DMA_CS_PRIORITY(7) | DMA_CS_PANIC_PRIORITY(7) | DMA_CS_DISDEBUG |DMA_CS_ACTIVE;
Started=true;
return 0;
}
int dma::stop()
{
dma_reg.gpioreg[DMA_CS+channel*0x40] = BCM2708_DMA_RESET;
usleep(1000);
dma_reg.gpioreg[DMA_CS+channel*0x40] = BCM2708_DMA_INT | BCM2708_DMA_END;
usleep(100);
dma_reg.gpioreg[DMA_CONBLK_AD+channel*0x40]=mem_virt_to_phys((void *)cbarray );
usleep(100);
dma_reg.gpioreg[DMA_DEBUG+channel*0x40] = 7; // clear debug error flags
usleep(100);
Started=false;
return 0;
}
int dma::getcbposition()
{
volatile uint32_t dmacb=(uint32_t)(dma_reg.gpioreg[DMA_CONBLK_AD+channel*0x40]);
//dbg_printf(1,"cb=%x\n",dmacb);
if(dmacb>0)
return mem_phys_to_virt(dmacb)-(size_t)virtbase;
else
return -1;
// dma_reg.gpioreg[DMA_CONBLK_AD+channel*0x40]-mem_virt_to_phys((void *)cbarray );
}
bool dma::isrunning()
{
return ((dma_reg.gpioreg[DMA_CS+channel*0x40]&DMA_CS_ACTIVE)>0);
}
bool dma::isunderflow()
{
//if((dma_reg.gpioreg[DMA_CS+channel*0x40]&DMA_CS_INT)>0) dbg_printf(1,"Status:%x\n",dma_reg.gpioreg[DMA_CS+channel*0x40]);
return ((dma_reg.gpioreg[DMA_CS+channel*0x40]&DMA_CS_INT)>0);
}
bool dma::SetCB(dma_cb_t *cbp,uint32_t dma_flag,uint32_t src,uint32_t dst,uint32_t repeat)
{
cbp->info = dma_flag;
cbp->src = src;
cbp->dst = dst;
cbp->length = 4*repeat;
cbp->stride = 0;
cbp->next = mem_virt_to_phys(cbp + 1);
return true;
}
bool dma::SetEasyCB(dma_cb_t *cbp,uint32_t index,dma_common_reg dst,uint32_t repeat)
{
uint32_t flag=BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP;
uint32_t src=mem_virt_to_phys(&usermem[index]);
switch(dst)
{
case dma_pllc_frac :break;
case dma_fsel:break;
case dma_pad:break;
case dma_pwm : flag|=BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(DREQ_PWM);break;
case dma_pcm : flag|=BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(DREQ_PCM_TX);break;
}
SetCB(cbp,flag,src,dst,repeat);
return true;
}
//**************************************** BUFFER DMA ********************************************************
bufferdma::bufferdma(int Channel,uint32_t tbuffersize,uint32_t tcbbysample,uint32_t tregisterbysample):dma(Channel,tbuffersize*tcbbysample,tbuffersize*tregisterbysample)
{
buffersize=tbuffersize;
cbbysample=tcbbysample;
registerbysample=tregisterbysample;
dbg_printf(1,"BufferSize %d , cb %d user %d\n",buffersize,buffersize*cbbysample,buffersize*registerbysample);
current_sample=0;
last_sample=0;
sample_available=buffersize;
sampletab=usermem;
}
void bufferdma::SetDmaAlgo()
{
}
int bufferdma::GetBufferAvailable()
{
int diffsample=0;
if(Started)
{
int CurrenCbPos=getcbposition();
if(CurrenCbPos!=-1)
{
current_sample=CurrenCbPos/(sizeof(dma_cb_t)*cbbysample);
}
else
{
dbg_printf(1,"DMA WEIRD STATE\n");
current_sample=0;
}
//dbg_printf(1,"CurrentCB=%d\n",current_sample);
diffsample=current_sample-last_sample;
if(diffsample<0) diffsample+=buffersize;
//dbg_printf(1,"cur %d last %d diff%d\n",current_sample,last_sample,diffsample);
}
else
{
//last_sample=buffersize-1;
diffsample=buffersize;
current_sample=0;
//dbg_printf(1,"Warning DMA stopped \n");
//dbg_printf(1,"S:cur %d last %d diff%d\n",current_sample,last_sample,diffsample);
}
/*
if(isunderflow())
{
dbg_printf(1,"cur %d last %d \n",current_sample,last_sample);
dbg_printf(1,"Underflow\n");
}*/
return diffsample;
}
int bufferdma::GetUserMemIndex()
{
int IndexAvailable=-1;
//dbg_printf(1,"Avail=%d\n",GetBufferAvailable());
if(GetBufferAvailable()>0)
{
IndexAvailable=last_sample+1;
if(IndexAvailable>=(int)buffersize) IndexAvailable=0;
}
return IndexAvailable;
}
int bufferdma::PushSample(int Index)
{
if(Index<0) return -1; // No buffer available
/*
dma_cb_t *cbp;
cbp=&cbarray[last_sample*cbbysample+cbbysample-1];
cbp->info=cbp->info&(~BCM2708_DMA_SET_INT);
*/
last_sample=Index;
/*
cbp=&cbarray[Index*cbbysample+cbbysample-1];
cbp->info=cbp->info|(BCM2708_DMA_SET_INT);
*/
if(Started==false)
{
if(last_sample>buffersize/4)
{
start(); // 1/4 Fill buffer before starting DMA
}
}
return 0;
}