Permalink
Newer
100644
145 lines (123 sloc)
3.95 KB
5
6
/**
7
* Audio wrapper for PWM on baremetal Pi.
8
*/
9
10
static void configure_pwm_clock() {
11
// disable pwm
12
PWM_CONTROL = 0;
13
// disable clock
14
PWMCLK_CNTL = 0x5a000000 | (PWMCLK_CNTL & ~(1 << 4));
15
// wait for busy flag to clear
16
while (PWMCLK_CNTL & (1 << 7));
17
// divide clock by 5.08626302083 to get to 2048*48KHz
18
PWMCLK_DIV = 0x5a005058;
19
// use PLLD @ 500MHz
20
PWMCLK_CNTL = 0x5a000006;
21
// enable clock (bit 4)
22
PWMCLK_CNTL = 0x5a000016;
23
// wait for busy flag to set
24
while (!(PWMCLK_CNTL & (1 << 7)));
28
// map gpio40 to pwm0 out (4 = alt function 0)
29
GPFSEL4 = (GPFSEL4 & ~(7)) | 4;
30
// map gpio45 to pwm1 out (4 = alt function 0)
31
GPFSEL4 = (GPFSEL4 & ~(7 << 15)) | (4 << 15);
32
PWM0_RANGE = PULSES_PER_SAMPLE;
33
PWM1_RANGE = PULSES_PER_SAMPLE;
34
35
// enable pwm0, use fifo for pwm0, enable pwm1, use fifo for pwm1
36
PWM_CONTROL = 1 + (1 << 5) + (1 << 8) + (1 << 13);
37
38
// enable dma, and raise dreq as soon as only 7 words left in the fifo
39
PWM_DMAC = 0x80000707;
43
unsigned int transfer_info;
44
unsigned int source_ad;
45
unsigned int dest_ad;
46
unsigned int txfr_len;
47
unsigned int stride;
48
unsigned int nextconblk;
49
unsigned int reserved1;
50
unsigned int reserved2;
51
} dma_cb_t;
52
53
// Defined in start.s so I can guarantee 8-byte alignment
54
extern dma_cb_t dma_control_blocks[];
55
unsigned int pwm_buffer0[BLOCK_SIZE];
56
unsigned int pwm_buffer1[BLOCK_SIZE];
57
unsigned int dma_cb0_addr;
58
unsigned int dma_cb1_addr;
59
unsigned int dma_last_filled;
60
61
static void configure_dma()
62
{
63
dma_cb0_addr = ((unsigned int) &dma_control_blocks[0]) + RAM_PHYS_OFFSET;
64
dma_cb1_addr = ((unsigned int) &dma_control_blocks[1]) + RAM_PHYS_OFFSET;
65
66
// 5 = pwm channel, 1 = src_inc, 4 = dest_dreq, 8 = wait_resp
67
dma_control_blocks[0].transfer_info = 0x00050148;
68
dma_control_blocks[0].source_ad = ((unsigned int) pwm_buffer0) + RAM_PHYS_OFFSET;
69
dma_control_blocks[0].dest_ad = 0x7E20C018; // PWM_FIFO physical address
70
dma_control_blocks[0].txfr_len = BLOCK_SIZE * sizeof(int);
71
dma_control_blocks[0].stride = 0;
72
dma_control_blocks[0].nextconblk = dma_cb1_addr;
73
dma_control_blocks[0].reserved1 = 0;
74
dma_control_blocks[0].reserved2 = 0;
75
76
dma_control_blocks[1].transfer_info = 0x00050148;
77
dma_control_blocks[1].source_ad = ((unsigned int) pwm_buffer1) + RAM_PHYS_OFFSET;
78
dma_control_blocks[1].dest_ad = 0x7E20C018;
79
dma_control_blocks[1].txfr_len = BLOCK_SIZE * sizeof(int);
80
dma_control_blocks[1].stride = 0;
81
dma_control_blocks[1].nextconblk = dma_cb0_addr;
82
dma_control_blocks[1].reserved1 = 0;
83
dma_control_blocks[1].reserved2 = 0;
84
85
dma_last_filled = 0;
86
87
// reset dma controller
88
DMA0_CS = 0x80000000;
100
// Wait for the buffers to flip
101
while (DMA0_CONBLK_AD != dma_last_filled);
102
103
unsigned int* pwm_buffer;
104
if (DMA0_CONBLK_AD == dma_cb0_addr) {
105
pwm_buffer = pwm_buffer1;
106
dma_last_filled = dma_cb1_addr;
107
} else {
108
pwm_buffer = pwm_buffer0;
109
dma_last_filled = dma_cb0_addr;
110
}
111
112
int i;
113
for (i = 0; i < BLOCK_SIZE; i++) {
114
pwm_buffer[i] = PULSES_PER_SAMPLE * ((1 + block[i]) / 2);
115
}
116
117
// Start DMA if not already active
118
if (!(DMA0_CS & 1)) {
119
// clear end flag
120
DMA0_CS |= 2;
121
122
// set control block
123
DMA0_CONBLK_AD = dma_last_filled;
124
125
// enable dma
126
DMA0_CS |= 1;
127
}
128
}
129
130
// Write PWM data via the FIFO register. Slow and no longer used.
131
void audio_write_fifo(float* block)
133
int i;
134
for (i = 0; i < BLOCK_SIZE; i++) {
135
int data = PULSES_PER_SAMPLE * ((1 + block[i]) / 2);
136
// wait for space in fifo
137
while (PWM_STATUS & 1);
138
// write data
139
PWM_FIFO = data;
140
}