altos/stm-vga: Implement VGA out from the STM processor
[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    = 481,
59         .vsync_end      = 484,
60         .vtotal         = 497
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_SPI2_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 = 13;
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].cndtr = SCANOUT;
98                 /* Enable */
99                 stm_dma.channel[DMA_INDEX].ccr = DMA_CCR(1);
100         }
101         stm_tim2.sr = ~(1 << STM_TIM234_SR_CC2IF);
102         line = stm_tim3.cnt;
103         if (vblank_off <= line && line < (AO_VGA_HEIGHT << 1) + vblank_off + 12) {
104                 vblank = 0;
105                 if ((line - vblank_off) & 1)
106                         scanline += AO_VGA_STRIDE;
107         } else {
108                 scanline = ao_vga_fb;
109                 vblank = 1;
110         }
111         stm_dma.channel[DMA_INDEX].cmar = scanline;
112         ao_arch_release_interrupts();
113 }
114
115 static void
116 ao_vga_fb_init(void)
117 {
118         ao_solid(0x0, AO_ALLONES,
119                  ao_vga_fb,
120                  AO_VGA_STRIDE,
121                  0,
122                  AO_VGA_WIDTH,
123                  AO_VGA_HEIGHT);
124
125         ao_solid(0x0, 0x0,
126                  ao_vga_fb + 10 * AO_VGA_STRIDE,
127                  AO_VGA_STRIDE,
128                  10,
129                  10,
130                  10);
131
132
133         ao_solid(0x0, 0x0,
134                  ao_vga_fb + 220 * AO_VGA_STRIDE,
135                  AO_VGA_STRIDE,
136                  10,
137                  10,
138                  10);
139
140         ao_solid(0x0, 0x0,
141                  ao_vga_fb + 10 * AO_VGA_STRIDE,
142                  AO_VGA_STRIDE,
143                  220,
144                  10,
145                  10);
146
147         ao_solid(0x0, 0x0,
148                  ao_vga_fb + 220 * AO_VGA_STRIDE,
149                  AO_VGA_STRIDE,
150                  220,
151                  10,
152                  10);
153
154         ao_text("Hello, Bdale!",
155                 ao_vga_fb + 100 * AO_VGA_STRIDE,
156                 AO_VGA_STRIDE,
157                 20);
158
159         ao_text("UL",
160                 ao_vga_fb,
161                 AO_VGA_STRIDE,
162                 0);
163
164         ao_text("BL",
165                 ao_vga_fb + (240 - 7) * AO_VGA_STRIDE,
166                 AO_VGA_STRIDE,
167                 0);
168 }
169
170 void
171 ao_vga_init(void)
172 {
173         /* Initialize spi2 using PB15 for output */
174         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
175
176         stm_ospeedr_set(&stm_gpiob, 15, STM_OSPEEDR_40MHz);
177         stm_afr_set(&stm_gpiob, 15, STM_AFR_AF5);
178
179         /* turn on SPI */
180         stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_SPI2EN);
181
182         stm_spi2.cr1 = ((1 << STM_SPI_CR1_BIDIMODE) |           /* Two wire mode */
183                         (1 << STM_SPI_CR1_BIDIOE) |
184                         (0 << STM_SPI_CR1_CRCEN) |              /* CRC disabled */
185                         (0 << STM_SPI_CR1_CRCNEXT) |
186                         (1 << STM_SPI_CR1_DFF) |
187                         (0 << STM_SPI_CR1_RXONLY) |
188                         (1 << STM_SPI_CR1_SSM) |                /* Software SS handling */
189                         (1 << STM_SPI_CR1_SSI) |                /*  ... */
190                         (1 << STM_SPI_CR1_LSBFIRST) |           /* Little endian */
191                         (1 << STM_SPI_CR1_SPE) |                /* Enable SPI unit */
192                         (0 << STM_SPI_CR1_BR) |                 /* baud rate to pclk/2 */
193                         (1 << STM_SPI_CR1_MSTR) |
194                         (0 << STM_SPI_CR1_CPOL) |               /* Format 0 */
195                         (0 << STM_SPI_CR1_CPHA));
196         stm_spi2.cr2 = ((0 << STM_SPI_CR2_TXEIE) |
197                         (0 << STM_SPI_CR2_RXNEIE) |
198                         (0 << STM_SPI_CR2_ERRIE) |
199                         (0 << STM_SPI_CR2_SSOE) |
200                         (1 << STM_SPI_CR2_TXDMAEN) |
201                         (0 << STM_SPI_CR2_RXDMAEN));
202
203         (void) stm_spi2.dr;
204         (void) stm_spi2.sr;
205
206         /* Grab the DMA channel for SPI2 MOSI */
207         stm_dma.channel[DMA_INDEX].cpar = &stm_spi2.dr;
208         stm_dma.channel[DMA_INDEX].cmar = ao_vga_fb;
209
210         /* hclock on timer 2 */
211
212         /* Turn on timer 2 */
213         stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_TIM2EN);
214
215         /* Turn on GPIOA */
216         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
217
218         stm_tim2.psc = 0;
219
220         /* Disable channels while modifying */
221         stm_tim2.ccer = 0;
222
223         /* Channel 1 hsync PWM values */
224         stm_tim2.ccr1 = mode.hsync_end - mode.hsync_start;
225
226         /* Channel 2 trigger scanout */
227         /* wait for the time to start scanout */
228         stm_tim2.ccr2 = 90;
229
230         stm_tim2.ccmr1 = ((0 << STM_TIM234_CCMR1_OC2CE) |
231                           (STM_TIM234_CCMR1_OC2M_SET_HIGH_ON_MATCH << STM_TIM234_CCMR1_OC2M)  |
232                           (1 << STM_TIM234_CCMR1_OC2PE) |
233                           (0 << STM_TIM234_CCMR1_OC2FE) |
234
235                           (0 << STM_TIM234_CCMR1_OC1CE) |
236                           (STM_TIM234_CCMR1_OC1M_PWM_MODE_1 << STM_TIM234_CCMR1_OC1M)  |
237                           (1 << STM_TIM234_CCMR1_OC1PE) |
238                           (0 << STM_TIM234_CCMR1_OC1FE) |
239                           (STM_TIM234_CCMR1_CC1S_OUTPUT << STM_TIM234_CCMR1_CC1S));
240
241         stm_tim2.arr = mode.htotal;
242         stm_tim2.cnt = 0;
243
244         /* Update the register contents */
245         stm_tim2.egr |= (1 << STM_TIM234_EGR_UG);
246
247         /* Enable the timer */
248
249         /* Enable the output */
250         stm_tim2.ccer = ((0 << STM_TIM234_CCER_CC1NP) |
251                          (STM_TIM234_CCER_CC1P_ACTIVE_LOW << STM_TIM234_CCER_CC1P) |
252                          (1 << STM_TIM234_CCER_CC1E));
253
254         stm_tim2.cr2 = ((0 << STM_TIM234_CR2_TI1S) |
255                         (STM_TIM234_CR2_MMS_UPDATE << STM_TIM234_CR2_MMS) |
256                         (0 << STM_TIM234_CR2_CCDS));
257
258         stm_tim2.smcr = 0;
259
260         stm_tim2.dier = ((1 << STM_TIM234_DIER_CC2IE));
261
262         stm_tim2.cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
263                         (1 << STM_TIM234_CR1_ARPE) |
264                         (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) |
265                         (STM_TIM234_CR1_DIR_UP << STM_TIM234_CR1_DIR) |
266                         (0 << STM_TIM234_CR1_OPM) |
267                         (1 << STM_TIM234_CR1_URS) |
268                         (0 << STM_TIM234_CR1_UDIS) |
269                         (0 << STM_TIM234_CR1_CEN));
270
271         /* Configure pins */
272
273         /* PA5 is Timer 2 CH1 output */
274         stm_ospeedr_set(&stm_gpioa, 5, STM_OSPEEDR_40MHz);
275         stm_afr_set(&stm_gpioa, 5, STM_AFR_AF1);
276
277         /* Turn on timer 3, slaved to timer 1 using ITR1 (table 61) */
278
279         /* Use CH1 on PB6 (AF2) */
280
281         stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_TIM3EN);
282
283         /* Turn on GPIOB */
284         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
285
286         /* No prescale */
287         stm_tim3.psc = 0;
288
289         /* Channel 1 vsync PWM values */
290         stm_tim3.ccr1 = mode.vsync_end - mode.vsync_start;
291         stm_tim3.ccmr1 = ((0 << STM_TIM234_CCMR1_OC2CE) |
292                           (0 << STM_TIM234_CCMR1_OC2PE) |
293                           (0 << STM_TIM234_CCMR1_OC2FE) |
294
295                           (0 << STM_TIM234_CCMR1_OC1CE) |
296                           (STM_TIM234_CCMR1_OC1M_PWM_MODE_1 << STM_TIM234_CCMR1_OC1M)  |
297                           (1 << STM_TIM234_CCMR1_OC1PE) |
298                           (0 << STM_TIM234_CCMR1_OC1FE) |
299                           (STM_TIM234_CCMR1_CC1S_OUTPUT << STM_TIM234_CCMR1_CC1S));
300
301         stm_tim3.arr = mode.vtotal;
302         stm_tim3.cnt = 0;
303
304         /* Update the register contents */
305         stm_tim3.egr |= (1 << STM_TIM234_EGR_UG);
306
307         /* Enable the timer */
308
309         /* Enable the output */
310         stm_tim3.ccer = ((0 << STM_TIM234_CCER_CC1NP) |
311                          (STM_TIM234_CCER_CC1P_ACTIVE_LOW << STM_TIM234_CCER_CC1P) |
312                          (1 << STM_TIM234_CCER_CC1E));
313
314         stm_tim3.cr2 = ((0 << STM_TIM234_CR2_TI1S) |
315                         (STM_TIM234_CR2_MMS_UPDATE << STM_TIM234_CR2_MMS) |
316                         (0 << STM_TIM234_CR2_CCDS));
317
318         stm_tim3.smcr = 0;
319         stm_tim3.smcr = ((0 << STM_TIM234_SMCR_ETP) |
320                          (0 << STM_TIM234_SMCR_ECE) |
321                          (STM_TIM234_SMCR_ETPS_OFF << STM_TIM234_SMCR_ETPS) |
322                          (STM_TIM234_SMCR_ETF_NONE << STM_TIM234_SMCR_ETF) |
323                          (0 << STM_TIM234_SMCR_MSM) |
324                          (STM_TIM234_SMCR_TS_ITR1 << STM_TIM234_SMCR_TS) |
325                          (0 << STM_TIM234_SMCR_OCCS) |
326                          (STM_TIM234_SMCR_SMS_EXTERNAL_CLOCK << STM_TIM234_SMCR_SMS));
327
328         stm_tim3.dier = 0;
329
330         stm_tim3.cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
331                         (1 << STM_TIM234_CR1_ARPE) |
332                         (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) |
333                         (STM_TIM234_CR1_DIR_UP << STM_TIM234_CR1_DIR) |
334                         (0 << STM_TIM234_CR1_OPM) |
335                         (1 << STM_TIM234_CR1_URS) |
336                         (0 << STM_TIM234_CR1_UDIS) |
337                         (1 << STM_TIM234_CR1_CEN));
338
339         /* Configure pins */
340
341         /* PB4 is Timer 3 CH1 output */
342         stm_ospeedr_set(&stm_gpiob, 4, STM_OSPEEDR_40MHz);
343         stm_afr_set(&stm_gpiob, 4, STM_AFR_AF2);
344
345
346         /* Enable the scanline interrupt */
347         stm_nvic_set_priority(STM_ISR_TIM2_POS, 0);
348         stm_nvic_set_enable(STM_ISR_TIM2_POS);
349 }
350
351 void
352 ao_vga_enable(int enable)
353 {
354         if (enable) {
355                 vblank_off = enable;
356                 ao_vga_fb_init();
357                 stm_tim2.cr1 |= (1 << STM_TIM234_CR1_CEN);
358                 stm_systick.csr &= ~(1 << STM_SYSTICK_CSR_ENABLE);
359         } else {
360                 stm_tim2.cr1 &= ~(1 << STM_TIM234_CR1_CEN);
361                 stm_systick.csr |= (1 << STM_SYSTICK_CSR_ENABLE);
362         }
363 }