altos/stm-vga: Fix DMA reset to load scanline each time
[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 struct ao_modeline {
21         long    dot_clock;      /* in Hz */
22
23         /* All timings are in pixels, with the first pixel out at 0,0 */
24         int     hactive;        /* active pixels */
25         int     hsync_start;    /* start of hsync pulse */
26         int     hsync_end;      /* end of hsync pulse */
27         int     htotal;         /* total h pixels */
28
29         int     vactive;        /* active scalines */
30         int     vsync_start;    /* start of vsync pulse */
31         int     vsync_end;      /* end of vsync pulse */
32         int     vtotal;         /* total scanlines */
33 };
34
35 const struct ao_modeline vga_640x480x60 = {
36         .dot_clock      = 23856000,     /* 23.86MHz dot, 29.82kHz line, 60.00Hz frame */
37
38         .hactive        = 640,
39         .hsync_start    = 656,
40         .hsync_end      = 720,
41         .htotal         = 800,
42
43         .vactive        = 480,
44         .vsync_start    = 481,
45         .vsync_end      = 484,
46         .vtotal         = 497
47 };
48
49 const struct ao_modeline vga_640x480x30 = {
50         .dot_clock      = 120000000,    /* 12.00MHz dot, 29.82kHz line, 30.00Hz frame */
51
52         .hactive        = 640,
53         .hsync_start    = 656,
54         .hsync_end      = 720,
55         .htotal         = 800,
56
57         .vactive        = 480,
58         .vsync_start    = 490,
59         .vsync_end      = 492,
60         .vtotal         = 525,
61 };
62
63 #define mode    vga_640x480x60
64
65 #define WIDTH_BYTES     (AO_VGA_WIDTH >> 3)
66 #define SCANOUT         ((WIDTH_BYTES+2) >> 1)
67
68 uint32_t        ao_vga_fb[AO_VGA_STRIDE * AO_VGA_HEIGHT];
69
70 static uint32_t *scanline;
71
72 #define DMA_INDEX       STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_TX)
73
74 static int      line;
75 static int      vblank;
76
77 #define DMA_CCR(en)     ((0 << STM_DMA_CCR_MEM2MEM) |                   \
78                          (STM_DMA_CCR_PL_VERY_HIGH << STM_DMA_CCR_PL) | \
79                          (STM_DMA_CCR_MSIZE_16 << STM_DMA_CCR_MSIZE) |  \
80                          (STM_DMA_CCR_PSIZE_16 << STM_DMA_CCR_PSIZE) |  \
81                          (1 << STM_DMA_CCR_MINC) |                      \
82                          (0 << STM_DMA_CCR_PINC) |                      \
83                          (0 << STM_DMA_CCR_CIRC) |                      \
84                          (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR) | \
85                          (0 << STM_DMA_CCR_TCIE) |                      \
86                          (en << STM_DMA_CCR_EN))
87
88 int vblank_off = 25;
89
90 void stm_tim2_isr(void)
91 {
92         ao_arch_block_interrupts();
93         if (!vblank) {
94                 /* Disable */
95                 stm_dma.channel[DMA_INDEX].ccr = DMA_CCR(0);
96                 /* Reset DMA engine for the next scanline */
97                 stm_dma.channel[DMA_INDEX].cmar = scanline;
98                 stm_dma.channel[DMA_INDEX].cndtr = SCANOUT;
99                 /* Enable */
100                 stm_dma.channel[DMA_INDEX].ccr = DMA_CCR(1);
101         }
102         stm_tim2.sr = ~(1 << STM_TIM234_SR_CC2IF);
103         line = stm_tim3.cnt;
104         if (vblank_off <= line && line < (AO_VGA_HEIGHT << 1) + vblank_off) {
105                 vblank = 0;
106                 if (((line - vblank_off) & 1) == 0)
107                         scanline += AO_VGA_STRIDE;
108         } else {
109                 if (!vblank) {
110                         stm_systick_isr();
111                         scanline = ao_vga_fb;
112                         vblank = 1;
113                 }
114         }
115         ao_arch_release_interrupts();
116 }
117
118 static void
119 ao_vga_fb_init(void)
120 {
121         ao_solid(0x0, AO_ALLONES,
122                  ao_vga_fb,
123                  AO_VGA_STRIDE,
124                  0,
125                  AO_VGA_WIDTH,
126                  AO_VGA_HEIGHT);
127
128         ao_solid(0x0, 0x0,
129                  ao_vga_fb + 10 * AO_VGA_STRIDE,
130                  AO_VGA_STRIDE,
131                  10,
132                  10,
133                  10);
134
135
136         ao_solid(0x0, 0x0,
137                  ao_vga_fb + 220 * AO_VGA_STRIDE,
138                  AO_VGA_STRIDE,
139                  10,
140                  10,
141                  10);
142
143         ao_solid(0x0, 0x0,
144                  ao_vga_fb + 10 * AO_VGA_STRIDE,
145                  AO_VGA_STRIDE,
146                  220,
147                  10,
148                  10);
149
150         ao_solid(0x0, 0x0,
151                  ao_vga_fb + 220 * AO_VGA_STRIDE,
152                  AO_VGA_STRIDE,
153                  220,
154                  10,
155                  10);
156
157         ao_text("Hello, Bdale!",
158                 ao_vga_fb + 100 * AO_VGA_STRIDE,
159                 AO_VGA_STRIDE,
160                 20);
161
162         ao_text("UL",
163                 ao_vga_fb,
164                 AO_VGA_STRIDE,
165                 1);
166
167         ao_text("BL",
168                 ao_vga_fb + (240 - 7) * AO_VGA_STRIDE,
169                 AO_VGA_STRIDE,
170                 1);
171
172         memset(ao_vga_fb + 120 * AO_VGA_STRIDE, '\0', WIDTH_BYTES);
173 }
174
175 void
176 ao_vga_init(void)
177 {
178         /* Initialize spi1 using PB5 for output */
179         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
180
181         stm_ospeedr_set(&stm_gpiob, 5, STM_OSPEEDR_40MHz);
182         stm_afr_set(&stm_gpiob, 5, STM_AFR_AF5);
183
184         /* turn on SPI */
185         stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SPI1EN);
186
187         stm_spi1.cr1 = ((1 << STM_SPI_CR1_BIDIMODE) |           /* Two wire mode */
188                         (1 << STM_SPI_CR1_BIDIOE) |
189                         (0 << STM_SPI_CR1_CRCEN) |              /* CRC disabled */
190                         (0 << STM_SPI_CR1_CRCNEXT) |
191                         (1 << STM_SPI_CR1_DFF) |
192                         (0 << STM_SPI_CR1_RXONLY) |
193                         (1 << STM_SPI_CR1_SSM) |                /* Software SS handling */
194                         (1 << STM_SPI_CR1_SSI) |                /*  ... */
195                         (1 << STM_SPI_CR1_LSBFIRST) |           /* Little endian */
196                         (1 << STM_SPI_CR1_SPE) |                /* Enable SPI unit */
197                         (0 << STM_SPI_CR1_BR) |                 /* baud rate to pclk/2 */
198                         (1 << STM_SPI_CR1_MSTR) |
199                         (0 << STM_SPI_CR1_CPOL) |               /* Format 0 */
200                         (0 << STM_SPI_CR1_CPHA));
201         stm_spi1.cr2 = ((0 << STM_SPI_CR2_TXEIE) |
202                         (0 << STM_SPI_CR2_RXNEIE) |
203                         (0 << STM_SPI_CR2_ERRIE) |
204                         (0 << STM_SPI_CR2_SSOE) |
205                         (1 << STM_SPI_CR2_TXDMAEN) |
206                         (0 << STM_SPI_CR2_RXDMAEN));
207
208         (void) stm_spi1.dr;
209         (void) stm_spi1.sr;
210
211         /* Grab the DMA channel for SPI1 MOSI */
212         stm_dma.channel[DMA_INDEX].cpar = &stm_spi1.dr;
213         stm_dma.channel[DMA_INDEX].cmar = ao_vga_fb;
214
215         /* hclock on timer 2 */
216
217         /* Turn on timer 2 */
218         stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_TIM2EN);
219
220         /* Turn on GPIOA */
221         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
222
223         stm_tim2.psc = 0;
224
225         /* Disable channels while modifying */
226         stm_tim2.ccer = 0;
227
228         /* Channel 1 hsync PWM values */
229         stm_tim2.ccr1 = mode.hsync_end - mode.hsync_start;
230
231         /* Channel 2 trigger scanout */
232         /* wait for the time to start scanout */
233         stm_tim2.ccr2 = mode.htotal - mode.hsync_end;
234
235         stm_tim2.ccmr1 = ((0 << STM_TIM234_CCMR1_OC2CE) |
236                           (STM_TIM234_CCMR1_OC2M_SET_HIGH_ON_MATCH << STM_TIM234_CCMR1_OC2M)  |
237                           (1 << STM_TIM234_CCMR1_OC2PE) |
238                           (0 << STM_TIM234_CCMR1_OC2FE) |
239
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));
245
246         stm_tim2.arr = mode.htotal;
247         stm_tim2.cnt = 0;
248
249         /* Update the register contents */
250         stm_tim2.egr |= (1 << STM_TIM234_EGR_UG);
251
252         /* Enable the timer */
253
254         /* Enable the output */
255         stm_tim2.ccer = ((0 << STM_TIM234_CCER_CC1NP) |
256                          (STM_TIM234_CCER_CC1P_ACTIVE_LOW << STM_TIM234_CCER_CC1P) |
257                          (1 << STM_TIM234_CCER_CC1E));
258
259         stm_tim2.cr2 = ((0 << STM_TIM234_CR2_TI1S) |
260                         (STM_TIM234_CR2_MMS_UPDATE << STM_TIM234_CR2_MMS) |
261                         (0 << STM_TIM234_CR2_CCDS));
262
263         stm_tim2.smcr = 0;
264
265         stm_tim2.dier = ((1 << STM_TIM234_DIER_CC2IE));
266
267         stm_tim2.cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
268                         (1 << STM_TIM234_CR1_ARPE) |
269                         (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) |
270                         (STM_TIM234_CR1_DIR_UP << STM_TIM234_CR1_DIR) |
271                         (0 << STM_TIM234_CR1_OPM) |
272                         (1 << STM_TIM234_CR1_URS) |
273                         (0 << STM_TIM234_CR1_UDIS) |
274                         (0 << STM_TIM234_CR1_CEN));
275
276         /* Configure pins */
277
278         /* PA5 is Timer 2 CH1 output */
279         stm_ospeedr_set(&stm_gpioa, 5, STM_OSPEEDR_40MHz);
280         stm_afr_set(&stm_gpioa, 5, STM_AFR_AF1);
281
282         /* Turn on timer 3, slaved to timer 1 using ITR1 (table 61) */
283
284         /* Use CH1 on PB6 (AF2) */
285
286         stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_TIM3EN);
287
288         /* Turn on GPIOB */
289         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
290
291         /* No prescale */
292         stm_tim3.psc = 0;
293
294         /* Channel 1 vsync PWM values */
295         stm_tim3.ccr1 = mode.vsync_end - mode.vsync_start;
296         stm_tim3.ccmr1 = ((0 << STM_TIM234_CCMR1_OC2CE) |
297                           (0 << STM_TIM234_CCMR1_OC2PE) |
298                           (0 << STM_TIM234_CCMR1_OC2FE) |
299
300                           (0 << STM_TIM234_CCMR1_OC1CE) |
301                           (STM_TIM234_CCMR1_OC1M_PWM_MODE_1 << STM_TIM234_CCMR1_OC1M)  |
302                           (1 << STM_TIM234_CCMR1_OC1PE) |
303                           (0 << STM_TIM234_CCMR1_OC1FE) |
304                           (STM_TIM234_CCMR1_CC1S_OUTPUT << STM_TIM234_CCMR1_CC1S));
305
306         stm_tim3.arr = mode.vtotal;
307         stm_tim3.cnt = 0;
308
309         /* Update the register contents */
310         stm_tim3.egr |= (1 << STM_TIM234_EGR_UG);
311
312         /* Enable the timer */
313
314         /* Enable the output */
315         stm_tim3.ccer = ((0 << STM_TIM234_CCER_CC1NP) |
316                          (STM_TIM234_CCER_CC1P_ACTIVE_LOW << STM_TIM234_CCER_CC1P) |
317                          (1 << STM_TIM234_CCER_CC1E));
318
319         stm_tim3.cr2 = ((0 << STM_TIM234_CR2_TI1S) |
320                         (STM_TIM234_CR2_MMS_UPDATE << STM_TIM234_CR2_MMS) |
321                         (0 << STM_TIM234_CR2_CCDS));
322
323         stm_tim3.smcr = 0;
324         stm_tim3.smcr = ((0 << STM_TIM234_SMCR_ETP) |
325                          (0 << STM_TIM234_SMCR_ECE) |
326                          (STM_TIM234_SMCR_ETPS_OFF << STM_TIM234_SMCR_ETPS) |
327                          (STM_TIM234_SMCR_ETF_NONE << STM_TIM234_SMCR_ETF) |
328                          (0 << STM_TIM234_SMCR_MSM) |
329                          (STM_TIM234_SMCR_TS_ITR1 << STM_TIM234_SMCR_TS) |
330                          (0 << STM_TIM234_SMCR_OCCS) |
331                          (STM_TIM234_SMCR_SMS_EXTERNAL_CLOCK << STM_TIM234_SMCR_SMS));
332
333         stm_tim3.dier = 0;
334
335         stm_tim3.cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
336                         (1 << STM_TIM234_CR1_ARPE) |
337                         (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) |
338                         (STM_TIM234_CR1_DIR_UP << STM_TIM234_CR1_DIR) |
339                         (0 << STM_TIM234_CR1_OPM) |
340                         (1 << STM_TIM234_CR1_URS) |
341                         (0 << STM_TIM234_CR1_UDIS) |
342                         (1 << STM_TIM234_CR1_CEN));
343
344         /* Configure pins */
345
346         /* PB4 is Timer 3 CH1 output */
347         stm_ospeedr_set(&stm_gpiob, 4, STM_OSPEEDR_40MHz);
348         stm_afr_set(&stm_gpiob, 4, STM_AFR_AF2);
349
350         /* Enable the scanline interrupt */
351         stm_nvic_set_priority(STM_ISR_TIM2_POS, 0);
352         stm_nvic_set_enable(STM_ISR_TIM2_POS);
353 }
354
355 void
356 ao_vga_enable(int enable)
357 {
358         if (enable) {
359                 vblank_off = enable;
360                 ao_vga_fb_init();
361                 stm_tim2.cr1 |= (1 << STM_TIM234_CR1_CEN);
362                 stm_systick.csr &= ~(1 << STM_SYSTICK_CSR_ENABLE);
363         } else {
364                 stm_tim2.cr1 &= ~(1 << STM_TIM234_CR1_CEN);
365                 stm_systick.csr |= (1 << STM_SYSTICK_CSR_ENABLE);
366         }
367 }