2 * Copyright © 2016 Keith Packard <keithp@keithp.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
18 /* VGA output from the SPI port
28 * pixel clock PA8 -> PB3
29 * pixel enable PA1 -> PA15
32 /* GRF formula for 640x480 yields a pixel clock very close to 24MHz. Pad by
33 * three scanlines to hit exactly that value
37 #define HSYNC_START (656)
38 #define HSYNC_END (720)
42 #define VSYNC_START 481
47 * The horizontal counter is set so that the end of hsync is reached
48 * at the maximum counter value. That means that the hblank interval
49 * is offset by HSYNC_END.
52 #define HSYNC (HSYNC_END - HSYNC_START)
53 #define HBLANK_END (HTOTAL - HSYNC_END)
54 #define HBLANK_START (HBLANK_END + HACTIVE)
57 * The vertical counter is set so that the end of vsync is reached at
58 * the maximum counter value. That means that the vblank interval is
59 * offset by VSYNC_END. We send a blank line at the start of the
60 * frame, so each of these is off by one
62 #define VSYNC (VSYNC_END - VSYNC_START)
63 #define VBLANK_END (VTOTAL - VSYNC_END)
64 #define VBLANK_START (VBLANK_END + VACTIVE)
66 #define WIDTH_BYTES (AO_VGA_WIDTH >> 3)
67 #define SCANOUT ((WIDTH_BYTES+2) >> 1)
69 uint32_t ao_vga_fb[AO_VGA_STRIDE * AO_VGA_HEIGHT];
71 const struct ao_bitmap ao_vga_bitmap = {
73 .stride = AO_VGA_STRIDE,
74 .width = AO_VGA_WIDTH,
75 .height = AO_VGA_HEIGHT
78 static uint32_t *scanline;
80 #define DMA_INDEX STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_TX)
82 #define DMA_CCR(en) ((0 << STM_DMA_CCR_MEM2MEM) | \
83 (STM_DMA_CCR_PL_VERY_HIGH << STM_DMA_CCR_PL) | \
84 (STM_DMA_CCR_MSIZE_16 << STM_DMA_CCR_MSIZE) | \
85 (STM_DMA_CCR_PSIZE_16 << STM_DMA_CCR_PSIZE) | \
86 (1 << STM_DMA_CCR_MINC) | \
87 (0 << STM_DMA_CCR_PINC) | \
88 (0 << STM_DMA_CCR_CIRC) | \
89 (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR) | \
90 (0 << STM_DMA_CCR_TCIE) | \
91 (en << STM_DMA_CCR_EN))
94 uint8_t ao_vga_vblank;
96 void stm_tim2_isr(void)
98 int16_t line = stm_tim3.cnt;
100 if (VBLANK_END <= line && line < VBLANK_START) {
102 stm_dma.channel[DMA_INDEX].ccr = DMA_CCR(0);
103 /* Reset DMA engine for the next scanline */
104 stm_dma.channel[DMA_INDEX].cmar = scanline;
105 stm_dma.channel[DMA_INDEX].cndtr = SCANOUT;
112 stm_dma.channel[DMA_INDEX].ccr = DMA_CCR(1);
113 if (((line - VBLANK_END) & 1))
114 scanline += AO_VGA_STRIDE;
116 if (line == VBLANK_START) {
118 ao_wakeup(&ao_vga_vblank);
120 scanline = ao_vga_fb;
131 /* Initialize spi1 using MISO PB4 for output */
132 stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
133 stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
135 stm_ospeedr_set(&stm_gpiob, 4, STM_OSPEEDR_40MHz);
136 stm_afr_set(&stm_gpiob, 4, STM_AFR_AF5);
137 stm_afr_set(&stm_gpiob, 3, STM_AFR_AF5);
138 stm_afr_set(&stm_gpioa, 15, STM_AFR_AF5);
141 stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SPI1EN);
143 stm_spi1.cr1 = ((1 << STM_SPI_CR1_BIDIMODE) | /* Two wire mode */
144 (1 << STM_SPI_CR1_BIDIOE) |
145 (0 << STM_SPI_CR1_CRCEN) | /* CRC disabled */
146 (0 << STM_SPI_CR1_CRCNEXT) |
147 (1 << STM_SPI_CR1_DFF) |
148 (0 << STM_SPI_CR1_RXONLY) | /* transmit, not receive */
149 (0 << STM_SPI_CR1_SSM) | /* Software SS handling */
150 (1 << STM_SPI_CR1_SSI) | /* ... */
151 (1 << STM_SPI_CR1_LSBFIRST) | /* Little endian */
152 (1 << STM_SPI_CR1_SPE) | /* Enable SPI unit */
153 (0 << STM_SPI_CR1_BR) | /* baud rate to pclk/2 */
154 (0 << STM_SPI_CR1_MSTR) | /* slave */
155 (0 << STM_SPI_CR1_CPOL) | /* Format 0 */
156 (0 << STM_SPI_CR1_CPHA));
157 stm_spi1.cr2 = ((0 << STM_SPI_CR2_TXEIE) |
158 (0 << STM_SPI_CR2_RXNEIE) |
159 (0 << STM_SPI_CR2_ERRIE) |
160 (0 << STM_SPI_CR2_SSOE) |
161 (1 << STM_SPI_CR2_TXDMAEN) |
162 (0 << STM_SPI_CR2_RXDMAEN));
167 /* Grab the DMA channel for SPI1 MOSI */
168 stm_dma.channel[DMA_INDEX].cpar = &stm_spi1.dr;
169 stm_dma.channel[DMA_INDEX].cmar = ao_vga_fb;
172 * Hsync Configuration
174 /* Turn on timer 2 */
175 stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_TIM2EN);
177 /* tim2 runs at full speed */
180 /* Disable channels while modifying */
183 /* Channel 1 hsync PWM values */
184 stm_tim2.ccr1 = HSYNC;
186 /* Channel 2 trigger scanout */
187 /* wait for the time to start scanout */
188 stm_tim2.ccr2 = HBLANK_END;
192 /* Configure channel 1 to output on the pin and
193 * channel 2 to to set the trigger for the vsync timer
195 stm_tim2.ccmr1 = ((0 << STM_TIM234_CCMR1_OC2CE) |
196 (STM_TIM234_CCMR1_OC2M_PWM_MODE_1 << STM_TIM234_CCMR1_OC2M) |
197 (1 << STM_TIM234_CCMR1_OC2PE) |
198 (0 << STM_TIM234_CCMR1_OC2FE) |
199 (STM_TIM234_CCMR1_CC2S_OUTPUT << STM_TIM234_CCMR1_CC2S) |
201 (0 << STM_TIM234_CCMR1_OC1CE) |
202 (STM_TIM234_CCMR1_OC1M_PWM_MODE_1 << STM_TIM234_CCMR1_OC1M) |
203 (1 << STM_TIM234_CCMR1_OC1PE) |
204 (0 << STM_TIM234_CCMR1_OC1FE) |
205 (STM_TIM234_CCMR1_CC1S_OUTPUT << STM_TIM234_CCMR1_CC1S));
207 stm_tim2.ccmr2 = ((0 << STM_TIM234_CCMR2_OC4CE) |
208 (0 << STM_TIM234_CCMR2_OC4M) |
209 (0 << STM_TIM234_CCMR2_OC4PE) |
210 (0 << STM_TIM234_CCMR2_OC4FE) |
211 (0 << STM_TIM234_CCMR2_CC4S) |
213 (0 << STM_TIM234_CCMR2_OC3CE) |
214 (STM_TIM234_CCMR2_OC3M_PWM_MODE_1 << STM_TIM234_CCMR2_OC3M) |
215 (1 << STM_TIM234_CCMR2_OC3PE) |
216 (0 << STM_TIM234_CCMR2_OC3FE) |
217 (0 << STM_TIM234_CCMR2_CC3S));
220 stm_tim2.arr = HTOTAL;
224 /* Update the register contents */
225 stm_tim2.egr |= (1 << STM_TIM234_EGR_UG);
227 /* Enable the timer */
229 /* Enable the output */
230 stm_tim2.ccer = ((0 << STM_TIM234_CCER_CC2NP) |
231 (STM_TIM234_CCER_CC2P_ACTIVE_HIGH << STM_TIM234_CCER_CC2P) |
232 (1 << STM_TIM234_CCER_CC2E) |
233 (0 << STM_TIM234_CCER_CC1NP) |
234 (STM_TIM234_CCER_CC1P_ACTIVE_LOW << STM_TIM234_CCER_CC1P) |
235 (1 << STM_TIM234_CCER_CC1E));
237 stm_tim2.cr2 = ((0 << STM_TIM234_CR2_TI1S) |
238 (STM_TIM234_CR2_MMS_UPDATE << STM_TIM234_CR2_MMS) |
239 (0 << STM_TIM234_CR2_CCDS));
241 /* hsync is not a slave timer */
244 /* Send an interrupt on channel 3 */
245 stm_tim2.dier = ((1 << STM_TIM234_DIER_CC3IE));
247 stm_tim2.cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
248 (1 << STM_TIM234_CR1_ARPE) |
249 (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) |
250 (STM_TIM234_CR1_DIR_UP << STM_TIM234_CR1_DIR) |
251 (0 << STM_TIM234_CR1_OPM) |
252 (1 << STM_TIM234_CR1_URS) |
253 (0 << STM_TIM234_CR1_UDIS) |
254 (0 << STM_TIM234_CR1_CEN));
256 /* Hsync is on PA5 which is Timer 2 CH1 output */
257 stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
258 stm_ospeedr_set(&stm_gpioa, 5, STM_OSPEEDR_40MHz);
259 stm_afr_set(&stm_gpioa, 5, STM_AFR_AF1);
261 /* pixel transmit enable is on PA1 */
262 stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
263 stm_ospeedr_set(&stm_gpioa, 1, STM_OSPEEDR_40MHz);
264 stm_afr_set(&stm_gpioa, 1, STM_AFR_AF1);
267 * Vsync configuration
270 /* Turn on timer 3, slaved to timer 1 using ITR1 (table 61) */
271 stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_TIM3EN);
276 /* Channel 1 or 2 vsync PWM values */
277 stm_tim3.ccr1 = VSYNC;
278 stm_tim3.ccr2 = VSYNC;
280 stm_tim3.ccmr1 = ((0 << STM_TIM234_CCMR1_OC2CE) |
281 (STM_TIM234_CCMR1_OC2M_PWM_MODE_1 << STM_TIM234_CCMR1_OC2M) |
282 (1 << STM_TIM234_CCMR1_OC2PE) |
283 (0 << STM_TIM234_CCMR1_OC2FE) |
284 (STM_TIM234_CCMR1_CC2S_OUTPUT << STM_TIM234_CCMR1_CC2S) |
286 (0 << STM_TIM234_CCMR1_OC1CE) |
287 (STM_TIM234_CCMR1_OC1M_PWM_MODE_1 << STM_TIM234_CCMR1_OC1M) |
288 (1 << STM_TIM234_CCMR1_OC1PE) |
289 (0 << STM_TIM234_CCMR1_OC1FE) |
290 (STM_TIM234_CCMR1_CC1S_OUTPUT << STM_TIM234_CCMR1_CC1S));
292 stm_tim3.arr = VTOTAL;
295 /* Update the register contents */
296 stm_tim3.egr |= (1 << STM_TIM234_EGR_UG);
298 /* Enable the timer */
300 /* Enable the output */
301 stm_tim3.ccer = ((0 << STM_TIM234_CCER_CC1NP) |
302 (STM_TIM234_CCER_CC2P_ACTIVE_LOW << STM_TIM234_CCER_CC2P) |
303 (1 << STM_TIM234_CCER_CC2E) |
304 (STM_TIM234_CCER_CC1P_ACTIVE_LOW << STM_TIM234_CCER_CC1P) |
305 (1 << STM_TIM234_CCER_CC1E));
307 stm_tim3.cr2 = ((0 << STM_TIM234_CR2_TI1S) |
308 (STM_TIM234_CR2_MMS_UPDATE << STM_TIM234_CR2_MMS) |
309 (0 << STM_TIM234_CR2_CCDS));
312 stm_tim3.smcr = ((0 << STM_TIM234_SMCR_ETP) |
313 (0 << STM_TIM234_SMCR_ECE) |
314 (STM_TIM234_SMCR_ETPS_OFF << STM_TIM234_SMCR_ETPS) |
315 (STM_TIM234_SMCR_ETF_NONE << STM_TIM234_SMCR_ETF) |
316 (0 << STM_TIM234_SMCR_MSM) |
317 (STM_TIM234_SMCR_TS_ITR1 << STM_TIM234_SMCR_TS) |
318 (0 << STM_TIM234_SMCR_OCCS) |
319 (STM_TIM234_SMCR_SMS_EXTERNAL_CLOCK << STM_TIM234_SMCR_SMS));
323 stm_tim3.cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
324 (1 << STM_TIM234_CR1_ARPE) |
325 (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) |
326 (STM_TIM234_CR1_DIR_UP << STM_TIM234_CR1_DIR) |
327 (0 << STM_TIM234_CR1_OPM) |
328 (1 << STM_TIM234_CR1_URS) |
329 (0 << STM_TIM234_CR1_UDIS) |
330 (1 << STM_TIM234_CR1_CEN));
332 /* Vsync is on PB5 which is is Timer 3 CH2 output */
333 stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
334 stm_ospeedr_set(&stm_gpiob, 5, STM_OSPEEDR_40MHz);
335 stm_afr_set(&stm_gpiob, 5, STM_AFR_AF2);
337 /* Use MCO for the pixel clock, that appears on PA8 */
338 cfgr = stm_rcc.cfgr & ~((STM_RCC_CFGR_MCOPRE_MASK << STM_RCC_CFGR_MCOPRE) |
339 (STM_RCC_CFGR_MCOSEL_MASK << STM_RCC_CFGR_MCOSEL));
341 cfgr |= ((STM_RCC_CFGR_MCOPRE_DIV_2 << STM_RCC_CFGR_MCOPRE) |
342 (STM_RCC_CFGR_MCOSEL_SYSCLK << STM_RCC_CFGR_MCOSEL));
346 stm_ospeedr_set(&stm_gpioa, 8, STM_OSPEEDR_40MHz);
347 stm_afr_set(&stm_gpioa, 8, STM_AFR_AF0);
349 /* Enable the scanline interrupt */
350 stm_nvic_set_priority(STM_ISR_TIM2_POS, AO_STM_NVIC_NONMASK_PRIORITY);
351 stm_nvic_set_enable(STM_ISR_TIM2_POS);
357 ao_vga_enable(int enable)
361 ++ao_task_minimize_latency;
364 stm_tim2.cr1 |= (1 << STM_TIM234_CR1_CEN);
367 --ao_task_minimize_latency;
370 stm_tim2.cr1 &= ~(1 << STM_TIM234_CR1_CEN);