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