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 */
20 /* GRF formula for 640x480 yields a pixel clock very close to 24MHz. Pad by
21 * three scanlines to hit exactly that value
25 #define HSYNC_START 656
30 #define VSYNC_START 481
35 * The horizontal counter is set so that the end of hsync is reached
36 * at the maximum counter value. That means that the hblank interval
37 * is offset by HSYNC_END. We send 16 bits of zeros (which looks like
38 * 32 pixels), so the start is offset by this much
41 #define HSYNC (HSYNC_END - HSYNC_START)
42 #define HBLANK_END (HTOTAL - HSYNC_END)
43 #define HBLANK_START (HBLANK_END + HACTIVE + 32)
46 * The vertical counter is set so that the end of vsync is reached at
47 * the maximum counter value. That means that the vblank interval is
48 * offset by VSYNC_END. We send a blank line at the start of the
49 * frame, so each of these is off by one
51 #define VSYNC (VSYNC_END - VSYNC_START)
52 #define VBLANK_END (VTOTAL - VSYNC_END - 1)
53 #define VBLANK_START (VBLANK_END + VACTIVE + 1)
55 #define WIDTH_BYTES (AO_VGA_WIDTH >> 3)
56 #define SCANOUT ((WIDTH_BYTES+2) >> 1)
58 uint32_t ao_vga_fb_all[AO_VGA_STRIDE * (AO_VGA_HEIGHT + AO_VGA_VPAD)];
60 const struct ao_bitmap ao_vga_bitmap = {
62 .stride = AO_VGA_STRIDE,
63 .width = AO_VGA_WIDTH,
64 .height = AO_VGA_HEIGHT
67 static uint32_t *scanline;
69 #define DMA_INDEX STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_TX)
74 #define DMA_CCR(en) ((0 << STM_DMA_CCR_MEM2MEM) | \
75 (STM_DMA_CCR_PL_VERY_HIGH << STM_DMA_CCR_PL) | \
76 (STM_DMA_CCR_MSIZE_16 << STM_DMA_CCR_MSIZE) | \
77 (STM_DMA_CCR_PSIZE_16 << STM_DMA_CCR_PSIZE) | \
78 (1 << STM_DMA_CCR_MINC) | \
79 (0 << STM_DMA_CCR_PINC) | \
80 (0 << STM_DMA_CCR_CIRC) | \
81 (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR) | \
82 (0 << STM_DMA_CCR_TCIE) | \
83 (en << STM_DMA_CCR_EN))
86 void stm_tim2_isr(void)
90 stm_dma.channel[DMA_INDEX].ccr = DMA_CCR(0);
91 /* Reset DMA engine for the next scanline */
92 stm_dma.channel[DMA_INDEX].cmar = scanline;
93 stm_dma.channel[DMA_INDEX].cndtr = SCANOUT;
95 stm_dma.channel[DMA_INDEX].ccr = DMA_CCR(1);
97 stm_tim2.sr = ~(1 << STM_TIM234_SR_CC2IF);
100 if (VBLANK_END <= line && line < VBLANK_START) {
102 if (((line - VBLANK_END) & 1))
103 scanline += AO_VGA_STRIDE;
104 } else if (!vblank) {
105 scanline = ao_vga_fb_all;
114 /* Initialize spi1 using PB5 for output */
115 stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
117 stm_ospeedr_set(&stm_gpiob, 5, STM_OSPEEDR_40MHz);
118 stm_afr_set(&stm_gpiob, 5, STM_AFR_AF5);
121 stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SPI1EN);
123 stm_spi1.cr1 = ((1 << STM_SPI_CR1_BIDIMODE) | /* Two wire mode */
124 (1 << STM_SPI_CR1_BIDIOE) |
125 (0 << STM_SPI_CR1_CRCEN) | /* CRC disabled */
126 (0 << STM_SPI_CR1_CRCNEXT) |
127 (1 << STM_SPI_CR1_DFF) |
128 (0 << STM_SPI_CR1_RXONLY) |
129 (1 << STM_SPI_CR1_SSM) | /* Software SS handling */
130 (1 << STM_SPI_CR1_SSI) | /* ... */
131 (1 << STM_SPI_CR1_LSBFIRST) | /* Little endian */
132 (1 << STM_SPI_CR1_SPE) | /* Enable SPI unit */
133 (0 << STM_SPI_CR1_BR) | /* baud rate to pclk/2 */
134 (1 << STM_SPI_CR1_MSTR) |
135 (0 << STM_SPI_CR1_CPOL) | /* Format 0 */
136 (0 << STM_SPI_CR1_CPHA));
137 stm_spi1.cr2 = ((0 << STM_SPI_CR2_TXEIE) |
138 (0 << STM_SPI_CR2_RXNEIE) |
139 (0 << STM_SPI_CR2_ERRIE) |
140 (0 << STM_SPI_CR2_SSOE) |
141 (1 << STM_SPI_CR2_TXDMAEN) |
142 (0 << STM_SPI_CR2_RXDMAEN));
147 /* Grab the DMA channel for SPI1 MOSI */
148 stm_dma.channel[DMA_INDEX].cpar = &stm_spi1.dr;
149 stm_dma.channel[DMA_INDEX].cmar = ao_vga_fb;
152 * Hsync Configuration
154 /* Turn on timer 2 */
155 stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_TIM2EN);
157 /* tim2 runs at full speed */
160 /* Disable channels while modifying */
163 /* Channel 1 hsync PWM values */
164 stm_tim2.ccr1 = HSYNC;
166 /* Channel 2 trigger scanout */
167 /* wait for the time to start scanout */
168 stm_tim2.ccr2 = HBLANK_END - 10;
170 /* Configure channel 1 to output on the pin and
171 * channel 2 to to set the trigger for the vsync timer
173 stm_tim2.ccmr1 = ((0 << STM_TIM234_CCMR1_OC2CE) |
174 (STM_TIM234_CCMR1_OC2M_SET_HIGH_ON_MATCH << STM_TIM234_CCMR1_OC2M) |
175 (1 << STM_TIM234_CCMR1_OC2PE) |
176 (0 << STM_TIM234_CCMR1_OC2FE) |
178 (0 << STM_TIM234_CCMR1_OC1CE) |
179 (STM_TIM234_CCMR1_OC1M_PWM_MODE_1 << STM_TIM234_CCMR1_OC1M) |
180 (1 << STM_TIM234_CCMR1_OC1PE) |
181 (0 << STM_TIM234_CCMR1_OC1FE) |
182 (STM_TIM234_CCMR1_CC1S_OUTPUT << STM_TIM234_CCMR1_CC1S));
185 stm_tim2.arr = HTOTAL;
189 /* Update the register contents */
190 stm_tim2.egr |= (1 << STM_TIM234_EGR_UG);
192 /* Enable the timer */
194 /* Enable the output */
195 stm_tim2.ccer = ((0 << STM_TIM234_CCER_CC1NP) |
196 (STM_TIM234_CCER_CC1P_ACTIVE_LOW << STM_TIM234_CCER_CC1P) |
197 (1 << STM_TIM234_CCER_CC1E));
199 stm_tim2.cr2 = ((0 << STM_TIM234_CR2_TI1S) |
200 (STM_TIM234_CR2_MMS_UPDATE << STM_TIM234_CR2_MMS) |
201 (0 << STM_TIM234_CR2_CCDS));
203 /* hsync is not a slave timer */
206 /* Send an interrupt on channel 2 */
207 stm_tim2.dier = ((1 << STM_TIM234_DIER_CC2IE));
209 stm_tim2.cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
210 (1 << STM_TIM234_CR1_ARPE) |
211 (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) |
212 (STM_TIM234_CR1_DIR_UP << STM_TIM234_CR1_DIR) |
213 (0 << STM_TIM234_CR1_OPM) |
214 (1 << STM_TIM234_CR1_URS) |
215 (0 << STM_TIM234_CR1_UDIS) |
216 (0 << STM_TIM234_CR1_CEN));
218 /* Hsync is on PA5 which is Timer 2 CH1 output */
219 stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
220 stm_ospeedr_set(&stm_gpioa, 5, STM_OSPEEDR_40MHz);
221 stm_afr_set(&stm_gpioa, 5, STM_AFR_AF1);
224 * Vsync configuration
227 /* Turn on timer 3, slaved to timer 1 using ITR1 (table 61) */
228 stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_TIM3EN);
233 /* Channel 1 vsync PWM values */
234 stm_tim3.ccr1 = VSYNC;
236 stm_tim3.ccmr1 = ((0 << STM_TIM234_CCMR1_OC2CE) |
237 (0 << STM_TIM234_CCMR1_OC2PE) |
238 (0 << STM_TIM234_CCMR1_OC2FE) |
240 (0 << STM_TIM234_CCMR1_OC1CE) |
241 (STM_TIM234_CCMR1_OC1M_PWM_MODE_1 << STM_TIM234_CCMR1_OC1M) |
242 (1 << STM_TIM234_CCMR1_OC1PE) |
243 (0 << STM_TIM234_CCMR1_OC1FE) |
244 (STM_TIM234_CCMR1_CC1S_OUTPUT << STM_TIM234_CCMR1_CC1S));
246 stm_tim3.arr = VTOTAL;
249 /* Update the register contents */
250 stm_tim3.egr |= (1 << STM_TIM234_EGR_UG);
252 /* Enable the timer */
254 /* Enable the output */
255 stm_tim3.ccer = ((0 << STM_TIM234_CCER_CC1NP) |
256 (STM_TIM234_CCER_CC1P_ACTIVE_LOW << STM_TIM234_CCER_CC1P) |
257 (1 << STM_TIM234_CCER_CC1E));
259 stm_tim3.cr2 = ((0 << STM_TIM234_CR2_TI1S) |
260 (STM_TIM234_CR2_MMS_UPDATE << STM_TIM234_CR2_MMS) |
261 (0 << STM_TIM234_CR2_CCDS));
264 stm_tim3.smcr = ((0 << STM_TIM234_SMCR_ETP) |
265 (0 << STM_TIM234_SMCR_ECE) |
266 (STM_TIM234_SMCR_ETPS_OFF << STM_TIM234_SMCR_ETPS) |
267 (STM_TIM234_SMCR_ETF_NONE << STM_TIM234_SMCR_ETF) |
268 (0 << STM_TIM234_SMCR_MSM) |
269 (STM_TIM234_SMCR_TS_ITR1 << STM_TIM234_SMCR_TS) |
270 (0 << STM_TIM234_SMCR_OCCS) |
271 (STM_TIM234_SMCR_SMS_EXTERNAL_CLOCK << STM_TIM234_SMCR_SMS));
275 stm_tim3.cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
276 (1 << STM_TIM234_CR1_ARPE) |
277 (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) |
278 (STM_TIM234_CR1_DIR_UP << STM_TIM234_CR1_DIR) |
279 (0 << STM_TIM234_CR1_OPM) |
280 (1 << STM_TIM234_CR1_URS) |
281 (0 << STM_TIM234_CR1_UDIS) |
282 (1 << STM_TIM234_CR1_CEN));
284 /* Vsync is on PB4 which is is Timer 3 CH1 output */
285 stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
286 stm_ospeedr_set(&stm_gpiob, 4, STM_OSPEEDR_40MHz);
287 stm_afr_set(&stm_gpiob, 4, STM_AFR_AF2);
289 /* Enable the scanline interrupt */
290 stm_nvic_set_priority(STM_ISR_TIM2_POS, AO_STM_NVIC_NON_MASK_PRIORITY);
291 stm_nvic_set_enable(STM_ISR_TIM2_POS);
297 ao_vga_enable(int enable)
301 ++ao_task_minimize_latency;
304 stm_tim2.cr1 |= (1 << STM_TIM234_CR1_CEN);
307 --ao_task_minimize_latency;
310 stm_tim2.cr1 &= ~(1 << STM_TIM234_CR1_CEN);