altos: Add vblank signal to vga driver
[fw/altos] / src / drivers / ao_vga.c
1 /*
2  * Copyright © 2016 Keith Packard <keithp@keithp.com>
3  *
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.
8  *
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.
13  */
14
15 #include "ao.h"
16 #include "ao_vga.h"
17
18 /* VGA output from the SPI port
19  *
20  * Connections:
21  *
22  *                      STM     VGA
23  *      GND                     4,6,7,8,9,10
24  *      HSYNC           PA5     13
25  *      VSYNC           PB5     14
26  *      RGB             PB4     1,2,3
27  *
28  *      pixel clock     PA8 -> PB3
29  *      pixel enable    PA1 -> PA15
30  */
31
32 /* GRF formula for 640x480 yields a pixel clock very close to 24MHz. Pad by
33  * three scanlines to hit exactly that value
34  */
35
36 #define HACTIVE         (640)
37 #define HSYNC_START     (656)
38 #define HSYNC_END       (720)
39 #define HTOTAL          (800)
40
41 #define VACTIVE         480
42 #define VSYNC_START     481
43 #define VSYNC_END       484
44 #define VTOTAL          500
45
46 /*
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.
50  */
51
52 #define HSYNC           (HSYNC_END - HSYNC_START)
53 #define HBLANK_END      (HTOTAL - HSYNC_END)
54 #define HBLANK_START    (HBLANK_END + HACTIVE)
55
56 /*
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
61  */
62 #define VSYNC           (VSYNC_END - VSYNC_START)
63 #define VBLANK_END      (VTOTAL - VSYNC_END)
64 #define VBLANK_START    (VBLANK_END + VACTIVE)
65
66 #define WIDTH_BYTES     (AO_VGA_WIDTH >> 3)
67 #define SCANOUT         ((WIDTH_BYTES+2) >> 1)
68
69 uint32_t        ao_vga_fb[AO_VGA_STRIDE * AO_VGA_HEIGHT];
70
71 const struct ao_bitmap ao_vga_bitmap = {
72         .base = ao_vga_fb,
73         .stride = AO_VGA_STRIDE,
74         .width = AO_VGA_WIDTH,
75         .height = AO_VGA_HEIGHT
76 };
77
78 static uint32_t *scanline;
79
80 #define DMA_INDEX       STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_TX)
81
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))
92
93
94 uint8_t ao_vga_vblank;
95
96 void stm_tim2_isr(void)
97 {
98         int16_t line = stm_tim3.cnt;
99
100         if (VBLANK_END <= line && line < VBLANK_START) {
101                 /* Disable */
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;
106
107                 /* reset SPI */
108                 (void) stm_spi1.dr;
109                 (void) stm_spi1.sr;
110
111                 /* Enable */
112                 stm_dma.channel[DMA_INDEX].ccr = DMA_CCR(1);
113                 if (((line - VBLANK_END) & 1))
114                         scanline += AO_VGA_STRIDE;
115         } else {
116                 if (line == VBLANK_START) {
117                         ao_vga_vblank = 1;
118                         ao_wakeup(&ao_vga_vblank);
119                 }
120                 scanline = ao_vga_fb;
121         }
122         stm_tim2.sr = 0;
123 }
124
125
126 void
127 ao_vga_init(void)
128 {
129         uint32_t        cfgr;
130
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);
134
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);
139
140         /* turn on SPI */
141         stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SPI1EN);
142
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));
163
164         (void) stm_spi1.dr;
165         (void) stm_spi1.sr;
166
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;
170
171         /*
172          * Hsync Configuration
173          */
174         /* Turn on timer 2 */
175         stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_TIM2EN);
176
177         /* tim2 runs at full speed */
178         stm_tim2.psc = 0;
179
180         /* Disable channels while modifying */
181         stm_tim2.ccer = 0;
182
183         /* Channel 1 hsync PWM values */
184         stm_tim2.ccr1 = HSYNC;
185
186         /* Channel 2 trigger scanout */
187         /* wait for the time to start scanout */
188         stm_tim2.ccr2 = HBLANK_END;
189
190         stm_tim2.ccr3 = 32;
191
192         /* Configure channel 1 to output on the pin and
193          * channel 2 to to set the trigger for the vsync timer
194          */
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) |
200
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));
206
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) |
212
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));
218
219         /* One scanline */
220         stm_tim2.arr = HTOTAL;
221
222         stm_tim2.cnt = 0;
223
224         /* Update the register contents */
225         stm_tim2.egr |= (1 << STM_TIM234_EGR_UG);
226
227         /* Enable the timer */
228
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));
236
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));
240
241         /* hsync is not a slave timer */
242         stm_tim2.smcr = 0;
243
244         /* Send an interrupt on channel 3 */
245         stm_tim2.dier = ((1 << STM_TIM234_DIER_CC3IE));
246
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));
255
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);
260
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);
265
266         /*
267          * Vsync configuration
268          */
269
270         /* Turn on timer 3, slaved to timer 1 using ITR1 (table 61) */
271         stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_TIM3EN);
272
273         /* No prescale */
274         stm_tim3.psc = 0;
275
276         /* Channel 1 or 2 vsync PWM values */
277         stm_tim3.ccr1 = VSYNC;
278         stm_tim3.ccr2 = VSYNC;
279
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) |
285
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));
291
292         stm_tim3.arr = VTOTAL;
293         stm_tim3.cnt = 0;
294
295         /* Update the register contents */
296         stm_tim3.egr |= (1 << STM_TIM234_EGR_UG);
297
298         /* Enable the timer */
299
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));
306
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));
310
311         stm_tim3.smcr = 0;
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));
320
321         stm_tim3.dier = 0;
322
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));
331
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);
336
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));
340
341         cfgr |= ((STM_RCC_CFGR_MCOPRE_DIV_2 << STM_RCC_CFGR_MCOPRE) |
342                  (STM_RCC_CFGR_MCOSEL_SYSCLK << STM_RCC_CFGR_MCOSEL));
343
344         stm_rcc.cfgr = cfgr;
345
346         stm_ospeedr_set(&stm_gpioa, 8, STM_OSPEEDR_40MHz);
347         stm_afr_set(&stm_gpioa, 8, STM_AFR_AF0);
348
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);
352 }
353
354 uint8_t enabled;
355
356 void
357 ao_vga_enable(int enable)
358 {
359         if (enable) {
360                 if (!enabled) {
361                         ++ao_task_minimize_latency;
362                         enabled = 1;
363                 }
364                 stm_tim2.cr1 |= (1 << STM_TIM234_CR1_CEN);
365         } else {
366                 if (enabled) {
367                         --ao_task_minimize_latency;
368                         enabled = 0;
369                 }
370                 stm_tim2.cr1 &= ~(1 << STM_TIM234_CR1_CEN);
371         }
372 }