/* 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 . */ #include "stdio.h" #include "atv.h" #include "gpio.h" #include #include #include #include #include atv::atv(uint64_t TuneFrequency, uint32_t SR, int Channel, uint32_t Lines) : bufferdma(Channel, 20 + /*(fixme)*/ 2 + Lines * (6 + (52 * 2)), 2, 1) // Need 2 more bytes for 0 and 1 // Need 6 CB more for sync, if so as 2CBby sample : 3 { SampleRate = SR; tunefreq = TuneFrequency; clkgpio::SetAdvancedPllMode(true); clkgpio::SetCenterFrequency(TuneFrequency, SampleRate); clkgpio::SetFrequency(0); clkgpio::enableclk(4); // GPIO 4 CLK by default syncwithpwm = true; if (syncwithpwm) { pwmgpio::SetPllNumber(clk_plld, 1); pwmgpio::SetFrequency(SampleRate); } else { pcmgpio::SetPllNumber(clk_plld, 1); pcmgpio::SetFrequency(SampleRate); } padgpio pad; Originfsel = pad.gpioreg[PADS_GPIO_0]; sampletab[(buffersize * registerbysample - 2)] = (0x5A << 24) + (1 & 0x7) + (1 << 4) + (0 << 3); // Amp 1 sampletab[(buffersize * registerbysample - 1)] = (0x5A << 24) + (0 & 0x7) + (1 << 4) + (0 << 3); // Amp 0 SetDmaAlgo(); } atv::~atv() { clkgpio::disableclk(4); padgpio pad; pad.gpioreg[PADS_GPIO_0] = Originfsel; } void atv::SetDmaAlgo() { dma_cb_t *cbp = cbarray; int LineResolution = 625; for (int frame = 0; frame < 2; frame++) { //32us*5 or 6 for (int i = 0; i < 5+frame; i++) { //2us 0,30us 1 //@0 //SYNC preegalisation 2us cbp->info = 0; //BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP ; cbp->src = mem_virt_to_phys(&usermem[(buffersize * registerbysample - 1)]); //Amp 0 cbp->dst = 0x7E000000 + (PADS_GPIO_0 << 2) + PADS_GPIO; cbp->length = 4; cbp->stride = 0; cbp->next = mem_virt_to_phys(cbp + 1); cbp++; // Delay if (syncwithpwm) cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP | BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(DREQ_PWM); else cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP | BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(DREQ_PCM_TX); cbp->src = mem_virt_to_phys(cbarray); // Data is not important as we use it only to feed the PWM if (syncwithpwm) cbp->dst = 0x7E000000 + (PWM_FIFO << 2) + PWM_BASE; else cbp->dst = 0x7E000000 + (PCM_FIFO_A << 2) + PCM_BASE; cbp->length = 4 * 2; //2us cbp->stride = 0; cbp->next = mem_virt_to_phys(cbp + 1); cbp++; //SYNC preegalisation 30us cbp->info = 0; //BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP ; cbp->src = mem_virt_to_phys(&usermem[(buffersize * registerbysample - 2)]); //Amp 1 cbp->dst = 0x7E000000 + (PADS_GPIO_0 << 2) + PADS_GPIO; cbp->length = 4; cbp->stride = 0; cbp->next = mem_virt_to_phys(cbp + 1); cbp++; // Delay if (syncwithpwm) cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP | BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(DREQ_PWM); else cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP | BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(DREQ_PCM_TX); cbp->src = mem_virt_to_phys(cbarray); // Data is not important as we use it only to feed the PWM if (syncwithpwm) cbp->dst = 0x7E000000 + (PWM_FIFO << 2) + PWM_BASE; else cbp->dst = 0x7E000000 + (PCM_FIFO_A << 2) + PCM_BASE; cbp->length = 4 * 30; //30us cbp->stride = 0; cbp->next = mem_virt_to_phys(cbp + 1); cbp++; } //SYNC top trame for (int i = 0; i < 5; i++) { cbp->info = 0; //BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP ; cbp->src = mem_virt_to_phys(&usermem[(buffersize * registerbysample - 1)]); //Amp 0 27us cbp->dst = 0x7E000000 + (PADS_GPIO_0 << 2) + PADS_GPIO; cbp->length = 4; cbp->stride = 0; cbp->next = mem_virt_to_phys(cbp + 1); cbp++; // Delay if (syncwithpwm) cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP | BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(DREQ_PWM); else cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP | BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(DREQ_PCM_TX); cbp->src = mem_virt_to_phys(cbarray); // Data is not important as we use it only to feed the PWM if (syncwithpwm) cbp->dst = 0x7E000000 + (PWM_FIFO << 2) + PWM_BASE; else cbp->dst = 0x7E000000 + (PCM_FIFO_A << 2) + PCM_BASE; cbp->length = 4 * 27; //27us cbp->stride = 0; cbp->next = mem_virt_to_phys(cbp + 1); cbp++; cbp->info = 0; //BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP ; cbp->src = mem_virt_to_phys(&usermem[(buffersize * registerbysample - 2)]); //Amp 1 5us cbp->dst = 0x7E000000 + (PADS_GPIO_0 << 2) + PADS_GPIO; cbp->length = 4; cbp->stride = 0; cbp->next = mem_virt_to_phys(cbp + 1); cbp++; // Delay if (syncwithpwm) cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP | BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(DREQ_PWM); else cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP | BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(DREQ_PCM_TX); cbp->src = mem_virt_to_phys(cbarray); // Data is not important as we use it only to feed the PWM if (syncwithpwm) cbp->dst = 0x7E000000 + (PWM_FIFO << 2) + PWM_BASE; else cbp->dst = 0x7E000000 + (PCM_FIFO_A << 2) + PCM_BASE; cbp->length = 4 * 5; //5us cbp->stride = 0; cbp->next = mem_virt_to_phys(cbp + 1); cbp++; } //postegalisation ; copy paste from preegalisation for (int i = 0; i < 5; i++) { //2us 0,30us 1 //@0 //SYNC preegalisation 2us cbp->info = 0; //BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP ; cbp->src = mem_virt_to_phys(&usermem[(buffersize * registerbysample - 1)]); //Amp 0 cbp->dst = 0x7E000000 + (PADS_GPIO_0 << 2) + PADS_GPIO; cbp->length = 4; cbp->stride = 0; cbp->next = mem_virt_to_phys(cbp + 1); cbp++; // Delay if (syncwithpwm) cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP | BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(DREQ_PWM); else cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP | BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(DREQ_PCM_TX); cbp->src = mem_virt_to_phys(cbarray); // Data is not important as we use it only to feed the PWM if (syncwithpwm) cbp->dst = 0x7E000000 + (PWM_FIFO << 2) + PWM_BASE; else cbp->dst = 0x7E000000 + (PCM_FIFO_A << 2) + PCM_BASE; cbp->length = 4 * 2; //2us cbp->stride = 0; cbp->next = mem_virt_to_phys(cbp + 1); cbp++; //SYNC preegalisation 30us cbp->info = 0; //BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP ; cbp->src = mem_virt_to_phys(&usermem[(buffersize * registerbysample - 2)]); //Amp 1 cbp->dst = 0x7E000000 + (PADS_GPIO_0 << 2) + PADS_GPIO; cbp->length = 4; cbp->stride = 0; cbp->next = mem_virt_to_phys(cbp + 1); cbp++; // Delay if (syncwithpwm) cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP | BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(DREQ_PWM); else cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP | BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(DREQ_PCM_TX); cbp->src = mem_virt_to_phys(cbarray); // Data is not important as we use it only to feed the PWM if (syncwithpwm) cbp->dst = 0x7E000000 + (PWM_FIFO << 2) + PWM_BASE; else cbp->dst = 0x7E000000 + (PCM_FIFO_A << 2) + PCM_BASE; cbp->length = 4 * 30; //30us cbp->stride = 0; cbp->next = mem_virt_to_phys(cbp + 1); cbp++; } for (int line = 0; line < 305; line++) { //@0 //SYNC 0/ 5us cbp->info = 0; //BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP ; cbp->src = mem_virt_to_phys(&usermem[(buffersize * registerbysample - 1)]); //Amp 0 cbp->dst = 0x7E000000 + (PADS_GPIO_0 << 2) + PADS_GPIO; cbp->length = 4; cbp->stride = 0; cbp->next = mem_virt_to_phys(cbp + 1); cbp++; // Delay if (syncwithpwm) cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP | BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(DREQ_PWM); else cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP | BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(DREQ_PCM_TX); cbp->src = mem_virt_to_phys(cbarray); // Data is not important as we use it only to feed the PWM if (syncwithpwm) cbp->dst = 0x7E000000 + (PWM_FIFO << 2) + PWM_BASE; else cbp->dst = 0x7E000000 + (PCM_FIFO_A << 2) + PCM_BASE; cbp->length = 4 * 4; //5us cbp->stride = 0; cbp->next = mem_virt_to_phys(cbp + 1); cbp++; //@0 //SYNC 1/ 5us cbp->info = 0; //BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP ; cbp->src = mem_virt_to_phys(&usermem[(buffersize * registerbysample - 2)]); //Amp 1 cbp->dst = 0x7E000000 + (PADS_GPIO_0 << 2) + PADS_GPIO; cbp->length = 4; cbp->stride = 0; cbp->next = mem_virt_to_phys(cbp + 1); cbp++; // Delay if (syncwithpwm) cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP | BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(DREQ_PWM); else cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP | BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(DREQ_PCM_TX); cbp->src = mem_virt_to_phys(cbarray); // Data is not important as we use it only to feed the PWM if (syncwithpwm) cbp->dst = 0x7E000000 + (PWM_FIFO << 2) + PWM_BASE; else cbp->dst = 0x7E000000 + (PCM_FIFO_A << 2) + PCM_BASE; cbp->length = 4 * 6; //5us; cbp->stride = 0; cbp->next = mem_virt_to_phys(cbp + 1); cbp++; for (uint32_t samplecnt = 0; samplecnt < 52; samplecnt++) //52 us { sampletab[samplecnt * registerbysample] = (0x5A << 24) + (3 & 0x7) + (1 << 4) + (0 << 3); // Amplitude PAD //@0 //DATA IN / 1us cbp->info = 0; //BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP ; cbp->src = mem_virt_to_phys(&usermem[samplecnt * registerbysample]); //Amp 1 cbp->dst = 0x7E000000 + (PADS_GPIO_0 << 2) + PADS_GPIO; cbp->length = 4; cbp->stride = 0; cbp->next = mem_virt_to_phys(cbp + 1); cbp++; // Delay if (syncwithpwm) cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP | BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(DREQ_PWM); else cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP | BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(DREQ_PCM_TX); cbp->src = mem_virt_to_phys(cbarray); // Data is not important as we use it only to feed the PWM if (syncwithpwm) cbp->dst = 0x7E000000 + (PWM_FIFO << 2) + PWM_BASE; else cbp->dst = 0x7E000000 + (PCM_FIFO_A << 2) + PCM_BASE; cbp->length = 4; //1us cbp->stride = 0; cbp->next = mem_virt_to_phys(cbp + 1); cbp++; } //FRONT PORSH //SYNC 2us cbp->info = 0; //BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP ; cbp->src = mem_virt_to_phys(&usermem[(buffersize * registerbysample - 2)]); //Amp 1 cbp->dst = 0x7E000000 + (PADS_GPIO_0 << 2) + PADS_GPIO; cbp->length = 4; cbp->stride = 0; cbp->next = mem_virt_to_phys(cbp + 1); cbp++; // Delay if (syncwithpwm) cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP | BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(DREQ_PWM); else cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP | BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(DREQ_PCM_TX); cbp->src = mem_virt_to_phys(cbarray); // Data is not important as we use it only to feed the PWM if (syncwithpwm) cbp->dst = 0x7E000000 + (PWM_FIFO << 2) + PWM_BASE; else cbp->dst = 0x7E000000 + (PCM_FIFO_A << 2) + PCM_BASE; cbp->length = 4 * 2; //2us cbp->stride = 0; cbp->next = mem_virt_to_phys(cbp + 1); cbp++; } } cbp--; cbp->next = mem_virt_to_phys(cbarray); // We loop to the first CB //fprintf(stderr,"Last cbp : src %x dest %x next %x\n",cbp->src,cbp->dst,cbp->next); } void atv::SetTvSample(uint32_t Index, float Amplitude) //-1;1 { Index = Index % buffersize; int IntAmplitude = round(abs(Amplitude) * 6.0) + 1; //1 to 7 int IntAmplitudePAD = IntAmplitude; if (IntAmplitudePAD > 7) IntAmplitudePAD = 7; if (IntAmplitudePAD < 0) IntAmplitudePAD = 0; //fprintf(stderr,"Amplitude=%f PAD %d\n",Amplitude,IntAmplitudePAD); sampletab[(Index)*registerbysample] = (0x5A << 24) + (IntAmplitudePAD & 0x7) + (1 << 4) + (0 << 3); // Amplitude PAD PushSample(Index); // ?? } void atv::SetTvSamples(float *sample, size_t Size) { size_t NbWritten = 0; int OSGranularity = 100; long int start_time; long time_difference = 0; struct timespec gettime_now; while (NbWritten < Size) { clock_gettime(CLOCK_REALTIME, &gettime_now); start_time = gettime_now.tv_nsec; int Available = GetBufferAvailable(); int TimeToSleep = 1e6 * ((int)buffersize * 3 / 4 - Available) / SampleRate - OSGranularity; // Sleep for theorically fill 3/4 of Fifo if (TimeToSleep > 0) { //fprintf(stderr,"buffer size %d Available %d SampleRate %d Sleep %d\n",buffersize,Available,SampleRate,TimeToSleep); usleep(TimeToSleep); } else { //fprintf(stderr,"No Sleep %d\n",TimeToSleep); sched_yield(); } clock_gettime(CLOCK_REALTIME, &gettime_now); time_difference = gettime_now.tv_nsec - start_time; if (time_difference < 0) time_difference += 1E9; //fprintf(stderr,"Measure samplerate=%d\n",(int)((GetBufferAvailable()-Available)*1e9/time_difference)); Available = GetBufferAvailable(); int Index = GetUserMemIndex(); int ToWrite = ((int)Size - (int)NbWritten) < Available ? Size - NbWritten : Available; for (int i = 0; i < ToWrite; i++) { SetTvSample(Index + i, sample[NbWritten++]); } } }