/ src / drivers / dvhstx / dvhstx.cpp
dvhstx.cpp
  1  #include <string.h>
  2  #include <pico/stdlib.h>
  3  
  4  extern "C" {
  5  #include <pico/lock_core.h>
  6  }
  7  
  8  #include <algorithm>
  9  #include "hardware/dma.h"
 10  #include "hardware/gpio.h"
 11  #include "hardware/irq.h"
 12  #include "hardware/structs/bus_ctrl.h"
 13  #include "hardware/structs/hstx_ctrl.h"
 14  #include "hardware/structs/hstx_fifo.h"
 15  #include "hardware/structs/sio.h"
 16  
 17  #include "hardware/structs/ioqspi.h"
 18  #include "hardware/vreg.h"
 19  #include "hardware/structs/qmi.h"
 20  #include "hardware/pll.h"
 21  #include "hardware/clocks.h"
 22  
 23  #include "dvi.hpp"
 24  #include "dvhstx.hpp"
 25  
 26  using namespace pimoroni;
 27  
 28  #ifdef MICROPY_BUILD_TYPE
 29  #define FRAME_BUFFER_SIZE (640*360)
 30  __attribute__((section(".uninitialized_data"))) static uint8_t frame_buffer_a[FRAME_BUFFER_SIZE];
 31  __attribute__((section(".uninitialized_data"))) static uint8_t frame_buffer_b[FRAME_BUFFER_SIZE];
 32  #endif
 33  
 34  #include "font.h"
 35  
 36  // If changing the font, note this code will not handle glyphs wider than 13 pixels
 37  #define FONT (&intel_one_mono)
 38  
 39  #ifdef MICROPY_BUILD_TYPE
 40  extern "C" {
 41  void dvhstx_debug(const char *fmt, ...);
 42  }
 43  #elif defined(ARDUINO)
 44  #include <Arduino.h>
 45  // #define dvhstx_debug Serial.printf
 46  #define dvhstx_debug(...) ((void)0)
 47  #else
 48  #include <cstdio>
 49  #define dvhstx_debug printf
 50  #endif
 51  
 52  static inline __attribute__((always_inline)) uint32_t render_char_line(int c, int y) {
 53      if (c < 0x20 || c > 0x7e) return 0;
 54      const lv_font_fmt_txt_glyph_dsc_t* g = &FONT->dsc->glyph_dsc[c - 0x20 + 1];
 55      const uint8_t *b = FONT->dsc->glyph_bitmap + g->bitmap_index;
 56      const int ey = y - FONT_HEIGHT + FONT->base_line + g->ofs_y + g->box_h;
 57      if (ey < 0 || ey >= g->box_h || g->box_w == 0) {
 58          return 0;
 59      }
 60      else {
 61          int bi = (g->box_w * ey);
 62  
 63          uint32_t bits = (b[bi >> 2] << 24) | (b[(bi >> 2) + 1] << 16) | (b[(bi >> 2) + 2] << 8) | b[(bi >> 2) + 3];
 64          bits >>= 6 - ((bi & 3) << 1);
 65          bits &= 0x3ffffff & (0x3ffffff << ((13 - g->box_w) << 1));
 66          bits >>= g->ofs_x << 1;
 67  
 68          return bits;
 69      }
 70  }
 71  
 72  // ----------------------------------------------------------------------------
 73  // HSTX command lists
 74  
 75  // Lists are padded with NOPs to be >= HSTX FIFO size, to avoid DMA rapidly
 76  // pingponging and tripping up the IRQs.
 77  
 78  static const uint32_t vblank_line_vsync_off_src[] = {
 79      HSTX_CMD_RAW_REPEAT,
 80      SYNC_V1_H1,
 81      HSTX_CMD_RAW_REPEAT,
 82      SYNC_V1_H0,
 83      HSTX_CMD_RAW_REPEAT,
 84      SYNC_V1_H1
 85  };
 86  static uint32_t vblank_line_vsync_off[count_of(vblank_line_vsync_off_src)];
 87  
 88  static const uint32_t vblank_line_vsync_on_src[] = {
 89      HSTX_CMD_RAW_REPEAT,
 90      SYNC_V0_H1,
 91      HSTX_CMD_RAW_REPEAT,
 92      SYNC_V0_H0,
 93      HSTX_CMD_RAW_REPEAT,
 94      SYNC_V0_H1
 95  };
 96  static uint32_t vblank_line_vsync_on[count_of(vblank_line_vsync_on_src)];
 97  
 98  static const uint32_t vactive_line_header_src[] = {
 99      HSTX_CMD_RAW_REPEAT,
100      SYNC_V1_H1,
101      HSTX_CMD_RAW_REPEAT,
102      SYNC_V1_H0,
103      HSTX_CMD_RAW_REPEAT,
104      SYNC_V1_H1,
105      HSTX_CMD_TMDS      
106  };
107  static uint32_t vactive_line_header[count_of(vactive_line_header_src)];
108  
109  static const uint32_t vactive_text_line_header_src[] = {
110      HSTX_CMD_RAW_REPEAT,
111      SYNC_V1_H1,
112      HSTX_CMD_RAW_REPEAT,
113      SYNC_V1_H0,
114      HSTX_CMD_RAW_REPEAT,
115      SYNC_V1_H1,
116      HSTX_CMD_RAW | 6,
117      BLACK_PIXEL_A,
118      BLACK_PIXEL_B,
119      BLACK_PIXEL_A,
120      BLACK_PIXEL_B,
121      BLACK_PIXEL_A,
122      BLACK_PIXEL_B,
123      HSTX_CMD_TMDS
124  };
125  static uint32_t vactive_text_line_header[count_of(vactive_text_line_header_src)];
126  
127  #define NUM_FRAME_LINES 2
128  #define NUM_CHANS 3
129  
130  static DVHSTX* display = nullptr;
131  
132  // ----------------------------------------------------------------------------
133  // DMA logic
134  
135  void __scratch_x("display") dma_irq_handler() {
136      display->gfx_dma_handler();
137  }
138  
139  void __scratch_x("display") DVHSTX::gfx_dma_handler() {
140      // ch_num indicates the channel that just finished, which is the one
141      // we're about to reload.
142      dma_channel_hw_t *ch = &dma_hw->ch[ch_num];
143      dma_hw->intr = 1u << ch_num;
144      if (++ch_num == NUM_CHANS) ch_num = 0;
145  
146      if (v_scanline >= timing_mode->v_front_porch && v_scanline < (timing_mode->v_front_porch + timing_mode->v_sync_width)) {
147          ch->read_addr = (uintptr_t)vblank_line_vsync_on;
148          ch->transfer_count = count_of(vblank_line_vsync_on);
149      } else if (v_scanline < v_inactive_total) {
150          ch->read_addr = (uintptr_t)vblank_line_vsync_off;
151          ch->transfer_count = count_of(vblank_line_vsync_off);
152      } else {
153          const int y = (v_scanline - v_inactive_total) >> v_repeat_shift;
154          const int new_line_num = (v_repeat_shift == 0) ? ch_num : (y & (NUM_FRAME_LINES - 1));
155          const uint line_buf_total_len = ((timing_mode->h_active_pixels * line_bytes_per_pixel) >> 2) + count_of(vactive_line_header);
156  
157          ch->read_addr = (uintptr_t)&line_buffers[new_line_num * line_buf_total_len];
158          ch->transfer_count = line_buf_total_len;
159  
160          // Fill line buffer
161          if (line_num != new_line_num)
162          {
163              line_num = new_line_num;
164              uint32_t* dst_ptr = &line_buffers[line_num * line_buf_total_len + count_of(vactive_line_header)];
165  
166              if (line_bytes_per_pixel == 2) {
167                  uint16_t* src_ptr = (uint16_t*)&frame_buffer_display[y * 2 * (timing_mode->h_active_pixels >> h_repeat_shift)];
168                  if (h_repeat_shift == 2) {
169                      for (int i = 0; i < timing_mode->h_active_pixels >> 1; i += 2) {
170                          uint32_t val = (uint32_t)(*src_ptr++) * 0x10001;
171                          *dst_ptr++ = val;
172                          *dst_ptr++ = val;
173                      }
174                  }
175                  else {
176                      for (int i = 0; i < timing_mode->h_active_pixels >> 1; ++i) {
177                          uint32_t val = (uint32_t)(*src_ptr++) * 0x10001;
178                          *dst_ptr++ = val;
179                      }
180                  }
181              }
182              else if (line_bytes_per_pixel == 1) {
183                  uint8_t* src_ptr = &frame_buffer_display[y * (timing_mode->h_active_pixels >> h_repeat_shift)];
184                  if (h_repeat_shift == 2) {
185                      for (int i = 0; i < timing_mode->h_active_pixels >> 2; ++i) {
186                          uint32_t val = (uint32_t)(*src_ptr++) * 0x01010101;
187                          *dst_ptr++ = val;
188                      }                
189                  }
190                  else {
191                      for (int i = 0; i < timing_mode->h_active_pixels >> 2; ++i) {
192                          uint32_t val = ((uint32_t)(*src_ptr++) * 0x0101);
193                          val |= ((uint32_t)(*src_ptr++) * 0x01010000);
194                          *dst_ptr++ = val;
195                      }
196                  }
197              }
198              else if (line_bytes_per_pixel == 4) {
199                  uint8_t* src_ptr = &frame_buffer_display[y * (timing_mode->h_active_pixels >> h_repeat_shift)];
200                  if (h_repeat_shift == 2) {
201                      for (int i = 0; i < timing_mode->h_active_pixels; i += 4) {
202                          uint32_t val = display_palette[*src_ptr++];
203                          *dst_ptr++ = val;
204                          *dst_ptr++ = val;
205                          *dst_ptr++ = val;
206                          *dst_ptr++ = val;
207                      }
208                  }
209                  else {
210                      for (int i = 0; i < timing_mode->h_active_pixels; i += 2) {
211                          uint32_t val = display_palette[*src_ptr++];
212                          *dst_ptr++ = val;
213                          *dst_ptr++ = val;
214                      }
215                  }
216              }
217          }
218      }
219  
220      if (++v_scanline == v_total_active_lines) {
221          v_scanline = 0;
222          line_num = -1;
223          if (flip_next) {
224              flip_next = false;
225              display->flip_now();
226          }
227          __sev();
228      }
229  }
230  
231  void __scratch_x("display") dma_irq_handler_text() {
232      display->text_dma_handler();
233  }
234  
235  void __scratch_x("display") DVHSTX::text_dma_handler() {
236      // ch_num indicates the channel that just finished, which is the one
237      // we're about to reload.
238      dma_channel_hw_t *ch = &dma_hw->ch[ch_num];
239      dma_hw->intr = 1u << ch_num;
240      if (++ch_num == NUM_CHANS) ch_num = 0;
241  
242      if (v_scanline >= timing_mode->v_front_porch && v_scanline < (timing_mode->v_front_porch + timing_mode->v_sync_width)) {
243          ch->read_addr = (uintptr_t)vblank_line_vsync_on;
244          ch->transfer_count = count_of(vblank_line_vsync_on);
245      } else if (v_scanline < v_inactive_total) {
246          ch->read_addr = (uintptr_t)vblank_line_vsync_off;
247          ch->transfer_count = count_of(vblank_line_vsync_off);
248      } else {
249          const int y = (v_scanline - v_inactive_total);
250          const uint line_buf_total_len = (frame_width * line_bytes_per_pixel + 3) / 4 + count_of(vactive_text_line_header);
251  
252          ch->read_addr = (uintptr_t)&line_buffers[ch_num * line_buf_total_len];
253          ch->transfer_count = line_buf_total_len;
254  
255          // Fill line buffer
256          int char_y = y % 24;
257          if (line_bytes_per_pixel == 4) {
258              uint32_t* dst_ptr = &line_buffers[ch_num * line_buf_total_len + count_of(vactive_text_line_header)];
259              uint8_t* src_ptr = &frame_buffer_display[(y / 24) * frame_width];
260              for (int i = 0; i < frame_width; ++i) {
261                  *dst_ptr++ = render_char_line(*src_ptr++, char_y);
262              }
263          }
264          else {
265              uint8_t* dst_ptr = (uint8_t*)&line_buffers[ch_num * line_buf_total_len + count_of(vactive_text_line_header)];
266              uint8_t* src_ptr = &frame_buffer_display[(y / 24) * frame_width * 2];
267  #ifdef __riscv
268              for (int i = 0; i < frame_width; ++i) {
269                  const uint8_t c = (*src_ptr++ - 0x20);
270                  uint32_t bits = (c < 95) ? font_cache[c * 24 + char_y] : 0;
271                  const uint8_t colour = *src_ptr++;
272  
273                  *dst_ptr++ = colour * ((bits >> 24) & 3);
274                  *dst_ptr++ = colour * ((bits >> 22) & 3);
275                  *dst_ptr++ = colour * ((bits >> 20) & 3);
276                  *dst_ptr++ = colour * ((bits >> 18) & 3);
277                  *dst_ptr++ = colour * ((bits >> 16) & 3);
278                  *dst_ptr++ = colour * ((bits >> 14) & 3);
279                  *dst_ptr++ = colour * ((bits >> 12) & 3);
280                  *dst_ptr++ = colour * ((bits >> 10) & 3);
281                  *dst_ptr++ = colour * ((bits >> 8) & 3);
282                  *dst_ptr++ = colour * ((bits >> 6) & 3);
283                  *dst_ptr++ = colour * ((bits >> 4) & 3);
284                  *dst_ptr++ = colour * ((bits >> 2) & 3);
285                  *dst_ptr++ = colour * (bits & 3);
286                  *dst_ptr++ = 0;
287              }
288  #else
289              int i = 0;
290              for (; i < frame_width-1; i += 2) {
291                  uint8_t c = (*src_ptr++ - 0x20);
292                  uint32_t bits = (c < 95) ? font_cache[c * 24 + char_y] : 0;
293                  uint8_t colour = *src_ptr++;
294                  c = (*src_ptr++ - 0x20);
295                  uint32_t bits2 = (c < 95) ? font_cache[c * 24 + char_y] : 0;
296                  uint8_t colour2 = *src_ptr++;
297  
298                  // This ASM works around a compiler bug where the optimizer decides
299                  // to unroll so hard it spills to the stack.
300                  uint32_t tmp, tmp2;
301                  asm volatile (
302                      "ubfx %[tmp], %[cbits], #24, #2\n\t"
303                      "ubfx %[tmp2], %[cbits], #22, #2\n\t"
304                      "bfi %[tmp], %[tmp2], #8, #8\n\t"
305                      "ubfx %[tmp2], %[cbits], #20, #2\n\t"
306                      "bfi %[tmp], %[tmp2], #16, #8\n\t"
307                      "ubfx %[tmp2], %[cbits], #18, #2\n\t"
308                      "bfi %[tmp], %[tmp2], #24, #8\n\t"
309                      "muls %[tmp], %[colour], %[tmp]\n\t"
310                      "str %[tmp], [%[dst_ptr]]\n\t"
311  
312                      "ubfx %[tmp], %[cbits], #16, #2\n\t"
313                      "ubfx %[tmp2], %[cbits], #14, #2\n\t"
314                      "bfi %[tmp], %[tmp2], #8, #8\n\t"
315                      "ubfx %[tmp2], %[cbits], #12, #2\n\t"
316                      "bfi %[tmp], %[tmp2], #16, #8\n\t"
317                      "ubfx %[tmp2], %[cbits], #10, #2\n\t"
318                      "bfi %[tmp], %[tmp2], #24, #8\n\t"
319                      "muls %[tmp], %[colour], %[tmp]\n\t"
320                      "str %[tmp], [%[dst_ptr], #4]\n\t"
321  
322                      "ubfx %[tmp], %[cbits], #8, #2\n\t"
323                      "ubfx %[tmp2], %[cbits], #6, #2\n\t"
324                      "bfi %[tmp], %[tmp2], #8, #8\n\t"
325                      "ubfx %[tmp2], %[cbits], #4, #2\n\t"
326                      "bfi %[tmp], %[tmp2], #16, #8\n\t"
327                      "ubfx %[tmp2], %[cbits], #2, #2\n\t"
328                      "bfi %[tmp], %[tmp2], #24, #8\n\t"
329                      "muls %[tmp], %[colour], %[tmp]\n\t"
330                      "str %[tmp], [%[dst_ptr], #8]\n\t"
331  
332                      "ubfx %[tmp], %[cbits2], #24, #2\n\t"
333                      "ubfx %[tmp2], %[cbits2], #22, #2\n\t"
334                      "bfi %[tmp], %[tmp2], #8, #8\n\t"
335                      "muls %[tmp], %[colour2], %[tmp]\n\t"
336                      "and %[tmp2], %[cbits], #3\n\t"
337                      "muls %[tmp2], %[colour], %[tmp2]\n\t"
338                      "bfi %[tmp2], %[tmp], #16, #16\n\t"
339                      "str %[tmp2], [%[dst_ptr], #12]\n\t"
340  
341                      "ubfx %[tmp], %[cbits2], #20, #2\n\t"
342                      "ubfx %[tmp2], %[cbits2], #18, #2\n\t"
343                      "bfi %[tmp], %[tmp2], #8, #8\n\t"
344                      "ubfx %[tmp2], %[cbits2], #16, #2\n\t"
345                      "bfi %[tmp], %[tmp2], #16, #8\n\t"
346                      "ubfx %[tmp2], %[cbits2], #14, #2\n\t"
347                      "bfi %[tmp], %[tmp2], #24, #8\n\t"
348                      "muls %[tmp], %[colour2], %[tmp]\n\t"
349                      "str %[tmp], [%[dst_ptr], #16]\n\t"
350  
351                      "ubfx %[tmp], %[cbits2], #12, #2\n\t"
352                      "ubfx %[tmp2], %[cbits2], #10, #2\n\t"
353                      "bfi %[tmp], %[tmp2], #8, #8\n\t"
354                      "ubfx %[tmp2], %[cbits2], #8, #2\n\t"
355                      "bfi %[tmp], %[tmp2], #16, #8\n\t"
356                      "ubfx %[tmp2], %[cbits2], #6, #2\n\t"
357                      "bfi %[tmp], %[tmp2], #24, #8\n\t"
358                      "muls %[tmp], %[colour2], %[tmp]\n\t"
359                      "str %[tmp], [%[dst_ptr], #20]\n\t"
360  
361                      "ubfx %[tmp], %[cbits2], #4, #2\n\t"
362                      "ubfx %[tmp2], %[cbits2], #2, #2\n\t"
363                      "bfi %[tmp], %[tmp2], #8, #8\n\t"
364                      "bfi %[tmp], %[cbits2], #16, #2\n\t"
365                      "muls %[tmp], %[colour2], %[tmp]\n\t"
366                      "str %[tmp], [%[dst_ptr], #24]\n\t"
367                      : [tmp] "=&l" (tmp),
368                        [tmp2] "=&l" (tmp2)
369                      : [cbits] "r" (bits),
370                        [colour] "l" (colour),
371                        [cbits2] "r" (bits2),
372                        [colour2] "l" (colour2),
373                        [dst_ptr] "r" (dst_ptr)
374                      : "cc", "memory" );
375                  dst_ptr += 14 * 2;                
376              }
377              if (i != frame_width) {
378                  const uint8_t c = (*src_ptr++ - 0x20);
379                  uint32_t bits = (c < 95) ? font_cache[c * 24 + char_y] : 0;
380                  const uint8_t colour = *src_ptr++;
381  
382                  *dst_ptr++ = colour * ((bits >> 24) & 3);
383                  *dst_ptr++ = colour * ((bits >> 22) & 3);
384                  *dst_ptr++ = colour * ((bits >> 20) & 3);
385                  *dst_ptr++ = colour * ((bits >> 18) & 3);
386                  *dst_ptr++ = colour * ((bits >> 16) & 3);
387                  *dst_ptr++ = colour * ((bits >> 14) & 3);
388                  *dst_ptr++ = colour * ((bits >> 12) & 3);
389                  *dst_ptr++ = colour * ((bits >> 10) & 3);
390                  *dst_ptr++ = colour * ((bits >> 8) & 3);
391                  *dst_ptr++ = colour * ((bits >> 6) & 3);
392                  *dst_ptr++ = colour * ((bits >> 4) & 3);
393                  *dst_ptr++ = colour * ((bits >> 2) & 3);
394                  *dst_ptr++ = colour * (bits & 3);
395                  *dst_ptr++ = 0;
396              }
397  #endif
398              if (y / 24 == cursor_y) {
399                  uint8_t* dst_ptr = (uint8_t*)&line_buffers[ch_num * line_buf_total_len + count_of(vactive_text_line_header)] + 14 * cursor_x;
400                  *dst_ptr++ ^= 0xff;
401                  *dst_ptr++ ^= 0xff;
402                  *dst_ptr++ ^= 0xff;
403                  *dst_ptr++ ^= 0xff;
404                  *dst_ptr++ ^= 0xff;
405                  *dst_ptr++ ^= 0xff;
406                  *dst_ptr++ ^= 0xff;
407                  *dst_ptr++ ^= 0xff;
408                  *dst_ptr++ ^= 0xff;
409                  *dst_ptr++ ^= 0xff;
410                  *dst_ptr++ ^= 0xff;
411                  *dst_ptr++ ^= 0xff;
412                  *dst_ptr++ ^= 0xff;
413              }
414          }
415      }
416  
417      if (++v_scanline == v_total_active_lines) {
418          v_scanline = 0;
419          line_num = -1;
420          if (flip_next) {
421              flip_next = false;
422              display->flip_now();
423          }
424          __sev();
425      }
426  }
427  
428  // ----------------------------------------------------------------------------
429  // Experimental clock config
430  
431  #ifndef MICROPY_BUILD_TYPE
432  static void __no_inline_not_in_flash_func(set_qmi_timing)() {
433      // Make sure flash is deselected - QMI doesn't appear to have a busy flag(!)
434      while ((ioqspi_hw->io[1].status & IO_QSPI_GPIO_QSPI_SS_STATUS_OUTTOPAD_BITS) != IO_QSPI_GPIO_QSPI_SS_STATUS_OUTTOPAD_BITS)
435          ;
436  
437      qmi_hw->m[0].timing = 0x40000202;
438      //qmi_hw->m[0].timing = 0x40000101;
439      // Force a read through XIP to ensure the timing is applied
440      volatile uint32_t* ptr = (volatile uint32_t*)0x14000000;
441      (void) *ptr;
442  }
443  #endif
444  
445  extern "C" void __no_inline_not_in_flash_func(display_setup_clock_preinit)() {
446      uint32_t intr_stash = save_and_disable_interrupts();
447  
448      // Before messing with clock speeds ensure QSPI clock is nice and slow
449      hw_write_masked(&qmi_hw->m[0].timing, 6, QMI_M0_TIMING_CLKDIV_BITS);
450  
451      // We're going to go fast, boost the voltage a little
452      vreg_set_voltage(VREG_VOLTAGE_1_15);
453  
454      // Force a read through XIP to ensure the timing is applied before raising the clock rate
455      volatile uint32_t* ptr = (volatile uint32_t*)0x14000000;
456      (void) *ptr;
457  
458      // Before we touch PLLs, switch sys and ref cleanly away from their aux sources.
459      hw_clear_bits(&clocks_hw->clk[clk_sys].ctrl, CLOCKS_CLK_SYS_CTRL_SRC_BITS);
460      while (clocks_hw->clk[clk_sys].selected != 0x1)
461          tight_loop_contents();
462      hw_write_masked(&clocks_hw->clk[clk_ref].ctrl, CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC, CLOCKS_CLK_REF_CTRL_SRC_BITS);
463      while (clocks_hw->clk[clk_ref].selected != 0x4)
464          tight_loop_contents();
465  
466      // Stop the other clocks so we don't worry about overspeed
467      clock_stop(clk_usb);
468      clock_stop(clk_adc);
469      clock_stop(clk_peri);
470      clock_stop(clk_hstx);
471  
472      // Set USB PLL to 528MHz
473      pll_init(pll_usb, PLL_COMMON_REFDIV, 1584 * MHZ, 3, 1);
474  
475      const uint32_t usb_pll_freq = 528 * MHZ;
476  
477      // CLK SYS = PLL USB 528MHz / 2 = 264MHz
478      clock_configure(clk_sys,
479                      CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX,
480                      CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
481                      usb_pll_freq, usb_pll_freq / 2);
482  
483      // CLK PERI = PLL USB 528MHz / 4 = 132MHz
484      clock_configure(clk_peri,
485                      0, // Only AUX mux on ADC
486                      CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
487                      usb_pll_freq, usb_pll_freq / 4);
488  
489      // CLK USB = PLL USB 528MHz / 11 = 48MHz
490      clock_configure(clk_usb,
491                      0, // No GLMUX
492                      CLOCKS_CLK_USB_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
493                      usb_pll_freq,
494                      USB_CLK_KHZ * KHZ);
495  
496      // CLK ADC = PLL USB 528MHz / 11 = 48MHz
497      clock_configure(clk_adc,
498                      0, // No GLMUX
499                      CLOCKS_CLK_ADC_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
500                      usb_pll_freq,
501                      USB_CLK_KHZ * KHZ);
502  
503      // Now we are running fast set fast QSPI clock and read delay
504      // On MicroPython this is setup by main.
505  #ifndef MICROPY_BUILD_TYPE
506      set_qmi_timing();
507  #endif
508  
509      restore_interrupts(intr_stash);
510  }
511  
512  #ifndef MICROPY_BUILD_TYPE
513  // Trigger clock setup early - on MicroPython this is done by a hook in main.
514  namespace {
515      class DV_preinit {
516          public:
517          DV_preinit() {
518              display_setup_clock_preinit();
519          }
520      };
521      DV_preinit dv_preinit __attribute__ ((init_priority (101))) ;
522  }
523  #endif
524  
525  void DVHSTX::display_setup_clock() {
526      const uint32_t dvi_clock_khz = timing_mode->bit_clk_khz >> 1;
527      uint vco_freq, post_div1, post_div2;
528      if (!check_sys_clock_khz(dvi_clock_khz, &vco_freq, &post_div1, &post_div2))
529          panic("System clock of %u kHz cannot be exactly achieved", dvi_clock_khz);
530      const uint32_t freq = vco_freq / (post_div1 * post_div2);
531  
532      // Set the sys PLL to the requested freq
533      pll_init(pll_sys, PLL_COMMON_REFDIV, vco_freq, post_div1, post_div2);
534  
535      // CLK HSTX = Requested freq
536      clock_configure(clk_hstx,
537                      0,
538                      CLOCKS_CLK_HSTX_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS,
539                      freq, freq);
540  }
541  
542  RGB888* DVHSTX::get_palette()
543  {
544      return palette;
545  }
546  
547  DVHSTX::DVHSTX()
548  {
549      // Always use the bottom channels
550      dma_claim_mask((1 << NUM_CHANS) - 1);
551  }
552  
553  bool DVHSTX::init(uint16_t width, uint16_t height, Mode mode_, bool double_buffered, const DVHSTXPinout &pinout)
554  {
555      if (inited) reset();
556  
557      cursor_y = -1;
558      ch_num = 0;
559      line_num = -1;
560      v_scanline = 2;
561      flip_next = false;
562  
563      display_width = width;
564      display_height = height;
565      frame_width = width;
566      frame_height = height;
567      mode = mode_;
568  
569      timing_mode = nullptr;
570      if (mode == MODE_TEXT_MONO || mode == MODE_TEXT_RGB111) {
571          width = 1280;
572          height = 720;
573          display_width = 91;
574          frame_width = 91;
575          display_height = 30;
576          frame_height = 30;
577          h_repeat_shift = 0;
578          v_repeat_shift = 0;
579          timing_mode = &dvi_timing_1280x720p_rb_50hz;
580      }
581      else if (width == 320 && height == 180) {
582          h_repeat_shift = 2;
583          v_repeat_shift = 2;
584          timing_mode = &dvi_timing_1280x720p_rb_50hz;
585      }
586      else if (width == 640 && height == 360) {
587          h_repeat_shift = 1;
588          v_repeat_shift = 1;
589          timing_mode = &dvi_timing_1280x720p_rb_50hz;
590      }
591      else if (width == 480 && height == 270) {
592          h_repeat_shift = 2;
593          v_repeat_shift = 2;
594          timing_mode = &dvi_timing_1920x1080p_rb2_30hz;
595      }
596      else
597      {
598          uint16_t full_width = display_width;
599          uint16_t full_height = display_height;
600          h_repeat_shift = 0;
601          v_repeat_shift = 0;
602  
603          if (display_width < 640) {
604              h_repeat_shift = 1;
605              full_width *= 2;
606          }
607  
608          if (display_height < 400) {
609              v_repeat_shift = 1;
610              full_height *= 2;
611          }
612  
613          if (full_width == 640) {
614              if (full_height == 480) timing_mode = &dvi_timing_640x480p_60hz;
615          }
616          else if (full_width == 720) {
617              if (full_height == 480) timing_mode = &dvi_timing_720x480p_60hz;
618              else if (full_height == 400) timing_mode = &dvi_timing_720x400p_70hz;
619              else if (full_height == 576) timing_mode = &dvi_timing_720x576p_50hz;
620          }
621          else if (full_width == 800) {
622              if (full_height == 600) timing_mode = &dvi_timing_800x600p_60hz;
623              else if (full_height == 480) timing_mode = &dvi_timing_800x480p_60hz;
624              else if (full_height == 450) timing_mode = &dvi_timing_800x450p_60hz;
625          }
626          else if (full_width == 960) {
627              if (full_height == 540) timing_mode = &dvi_timing_960x540p_60hz;
628          }
629          else if (full_width == 1024) {
630              if (full_height == 768) timing_mode = &dvi_timing_1024x768_rb_60hz;
631          }
632      }
633  
634      if (!timing_mode) {
635          dvhstx_debug("Unsupported resolution %dx%d", width, height);
636          return false;
637      }
638  
639      display = this;
640      display_palette = get_palette();
641      
642      dvhstx_debug("Setup clock\n");
643      display_setup_clock();
644  
645  #if !defined(MICROPY_BUILD_TYPE) && !defined(ARDUINO)
646      stdio_init_all();
647  #endif
648      dvhstx_debug("Clock setup done\n");
649  
650      v_inactive_total = timing_mode->v_front_porch + timing_mode->v_sync_width + timing_mode->v_back_porch;
651      v_total_active_lines = v_inactive_total + timing_mode->v_active_lines;
652      v_repeat = 1 << v_repeat_shift;
653      h_repeat = 1 << h_repeat_shift;
654  
655      memcpy(vblank_line_vsync_off, vblank_line_vsync_off_src, sizeof(vblank_line_vsync_off_src));
656      vblank_line_vsync_off[0] |= timing_mode->h_front_porch;
657      vblank_line_vsync_off[2] |= timing_mode->h_sync_width;
658      vblank_line_vsync_off[4] |= timing_mode->h_back_porch + timing_mode->h_active_pixels;
659  
660      memcpy(vblank_line_vsync_on, vblank_line_vsync_on_src, sizeof(vblank_line_vsync_on_src));
661      vblank_line_vsync_on[0] |= timing_mode->h_front_porch;
662      vblank_line_vsync_on[2] |= timing_mode->h_sync_width;
663      vblank_line_vsync_on[4] |= timing_mode->h_back_porch + timing_mode->h_active_pixels;
664  
665      memcpy(vactive_line_header, vactive_line_header_src, sizeof(vactive_line_header_src));
666      vactive_line_header[0] |= timing_mode->h_front_porch;
667      vactive_line_header[2] |= timing_mode->h_sync_width;
668      vactive_line_header[4] |= timing_mode->h_back_porch;
669      vactive_line_header[6] |= timing_mode->h_active_pixels;
670  
671      memcpy(vactive_text_line_header, vactive_text_line_header_src, sizeof(vactive_text_line_header_src));
672      vactive_text_line_header[0] |= timing_mode->h_front_porch;
673      vactive_text_line_header[2] |= timing_mode->h_sync_width;
674      vactive_text_line_header[4] |= timing_mode->h_back_porch;
675      vactive_text_line_header[7+6] |= timing_mode->h_active_pixels - 6;
676  
677      switch (mode) {
678      case MODE_RGB565:
679          frame_bytes_per_pixel = 2;
680          line_bytes_per_pixel = 2;
681          break;
682      case MODE_PALETTE:
683          frame_bytes_per_pixel = 1;
684          line_bytes_per_pixel = 4;
685          break;
686      case MODE_RGB888:
687          frame_bytes_per_pixel = 4;
688          line_bytes_per_pixel = 4;
689          break;
690      case MODE_TEXT_MONO:
691          frame_bytes_per_pixel = 1;
692          line_bytes_per_pixel = 4;
693          break;
694      case MODE_TEXT_RGB111:
695          frame_bytes_per_pixel = 2;
696          line_bytes_per_pixel = 14;
697          break;
698      default:
699          dvhstx_debug("Unsupported mode %d", (int)mode);
700          return false;
701      }
702  
703  #ifdef MICROPY_BUILD_TYPE
704      if (frame_width * frame_height * frame_bytes_per_pixel > sizeof(frame_buffer_a)) {
705          panic("Frame buffer too large");
706      }
707  
708      frame_buffer_display = frame_buffer_a;
709      frame_buffer_back = double_buffered ? frame_buffer_b : frame_buffer_a;
710  #else
711      frame_buffer_display = (uint8_t*)malloc(frame_width * frame_height * frame_bytes_per_pixel);
712      frame_buffer_back = double_buffered ? (uint8_t*)malloc(frame_width * frame_height * frame_bytes_per_pixel) : frame_buffer_display;
713  #endif
714      memset(frame_buffer_display, 0, frame_width * frame_height * frame_bytes_per_pixel);
715      memset(frame_buffer_back, 0, frame_width * frame_height * frame_bytes_per_pixel);
716  
717      memset(palette, 0, PALETTE_SIZE * sizeof(palette[0]));
718  
719      frame_buffer_display = frame_buffer_display;
720      dvhstx_debug("Frame buffers inited\n");
721  
722      const bool is_text_mode = (mode == MODE_TEXT_MONO || mode == MODE_TEXT_RGB111);
723      const int frame_pixel_words = (frame_width * h_repeat * line_bytes_per_pixel + 3) >> 2;
724      const int frame_line_words = frame_pixel_words + (is_text_mode ? count_of(vactive_text_line_header) : count_of(vactive_line_header));
725      const int frame_lines = (v_repeat == 1) ? NUM_CHANS : NUM_FRAME_LINES;
726      line_buffers = (uint32_t*)malloc(frame_line_words * 4 * frame_lines);
727  
728      for (int i = 0; i < frame_lines; ++i)
729      {
730          if (is_text_mode) memcpy(&line_buffers[i * frame_line_words], vactive_text_line_header, count_of(vactive_text_line_header) * sizeof(uint32_t));
731          else memcpy(&line_buffers[i * frame_line_words], vactive_line_header, count_of(vactive_line_header) * sizeof(uint32_t));
732      }
733  
734      if (mode == MODE_TEXT_RGB111) {
735          // Need to pre-render the font to RAM to be fast enough.
736          font_cache = (uint32_t*)malloc(4 * FONT->line_height * 96);
737          uint32_t* font_cache_ptr = font_cache;
738          for (int c = 0x20; c < 128; ++c) {
739              for (int y = 0; y < FONT->line_height; ++y) {
740                  *font_cache_ptr++ = render_char_line(c, y);
741              }
742          }
743      }
744  
745      // Ensure HSTX FIFO is clear
746      reset_block_num(RESET_HSTX);
747      sleep_us(10);
748      unreset_block_num_wait_blocking(RESET_HSTX);
749      sleep_us(10);
750  
751      switch (mode) {
752      case MODE_RGB565:
753          // Configure HSTX's TMDS encoder for RGB565
754          hstx_ctrl_hw->expand_tmds =
755              4  << HSTX_CTRL_EXPAND_TMDS_L2_NBITS_LSB |
756              8 << HSTX_CTRL_EXPAND_TMDS_L2_ROT_LSB   |
757              5  << HSTX_CTRL_EXPAND_TMDS_L1_NBITS_LSB |
758              3  << HSTX_CTRL_EXPAND_TMDS_L1_ROT_LSB   |
759              4  << HSTX_CTRL_EXPAND_TMDS_L0_NBITS_LSB |
760              29 << HSTX_CTRL_EXPAND_TMDS_L0_ROT_LSB;
761  
762          // Pixels (TMDS) come in 2 16-bit chunks. Control symbols (RAW) are an
763          // entire 32-bit word.
764          hstx_ctrl_hw->expand_shift =
765              2 << HSTX_CTRL_EXPAND_SHIFT_ENC_N_SHIFTS_LSB |
766              16 << HSTX_CTRL_EXPAND_SHIFT_ENC_SHIFT_LSB |
767              1 << HSTX_CTRL_EXPAND_SHIFT_RAW_N_SHIFTS_LSB |
768              0 << HSTX_CTRL_EXPAND_SHIFT_RAW_SHIFT_LSB;
769          break;
770  
771      case MODE_PALETTE:
772          // Configure HSTX's TMDS encoder for RGB888
773          hstx_ctrl_hw->expand_tmds =
774              7  << HSTX_CTRL_EXPAND_TMDS_L2_NBITS_LSB |
775              16 << HSTX_CTRL_EXPAND_TMDS_L2_ROT_LSB   |
776              7  << HSTX_CTRL_EXPAND_TMDS_L1_NBITS_LSB |
777              8  << HSTX_CTRL_EXPAND_TMDS_L1_ROT_LSB   |
778              7  << HSTX_CTRL_EXPAND_TMDS_L0_NBITS_LSB |
779              0  << HSTX_CTRL_EXPAND_TMDS_L0_ROT_LSB;
780  
781          // Pixels and control symbols (RAW) are an
782          // entire 32-bit word.
783          hstx_ctrl_hw->expand_shift =
784              1 << HSTX_CTRL_EXPAND_SHIFT_ENC_N_SHIFTS_LSB |
785              0 << HSTX_CTRL_EXPAND_SHIFT_ENC_SHIFT_LSB |
786              1 << HSTX_CTRL_EXPAND_SHIFT_RAW_N_SHIFTS_LSB |
787              0 << HSTX_CTRL_EXPAND_SHIFT_RAW_SHIFT_LSB;
788          break;
789  
790      case MODE_TEXT_MONO:
791          // Configure HSTX's TMDS encoder for 2bpp
792          hstx_ctrl_hw->expand_tmds =
793              1  << HSTX_CTRL_EXPAND_TMDS_L2_NBITS_LSB |
794              18 << HSTX_CTRL_EXPAND_TMDS_L2_ROT_LSB   |
795              1  << HSTX_CTRL_EXPAND_TMDS_L1_NBITS_LSB |
796              18  << HSTX_CTRL_EXPAND_TMDS_L1_ROT_LSB   |
797              1  << HSTX_CTRL_EXPAND_TMDS_L0_NBITS_LSB |
798              18  << HSTX_CTRL_EXPAND_TMDS_L0_ROT_LSB;
799  
800          // Pixels and control symbols (RAW) are an
801          // entire 32-bit word.
802          hstx_ctrl_hw->expand_shift =
803              14 << HSTX_CTRL_EXPAND_SHIFT_ENC_N_SHIFTS_LSB |
804              30 << HSTX_CTRL_EXPAND_SHIFT_ENC_SHIFT_LSB |
805              1 << HSTX_CTRL_EXPAND_SHIFT_RAW_N_SHIFTS_LSB |
806              0 << HSTX_CTRL_EXPAND_SHIFT_RAW_SHIFT_LSB;
807          break;
808  
809      case MODE_TEXT_RGB111:
810          // Configure HSTX's TMDS encoder for RGB222
811          hstx_ctrl_hw->expand_tmds =
812              1  << HSTX_CTRL_EXPAND_TMDS_L2_NBITS_LSB |
813              0 << HSTX_CTRL_EXPAND_TMDS_L2_ROT_LSB   |
814              1  << HSTX_CTRL_EXPAND_TMDS_L1_NBITS_LSB |
815              29  << HSTX_CTRL_EXPAND_TMDS_L1_ROT_LSB   |
816              1  << HSTX_CTRL_EXPAND_TMDS_L0_NBITS_LSB |
817              26 << HSTX_CTRL_EXPAND_TMDS_L0_ROT_LSB;
818  
819          // Pixels (TMDS) come in 4 8-bit chunks. Control symbols (RAW) are an
820          // entire 32-bit word.
821          hstx_ctrl_hw->expand_shift =
822              4 << HSTX_CTRL_EXPAND_SHIFT_ENC_N_SHIFTS_LSB |
823              8 << HSTX_CTRL_EXPAND_SHIFT_ENC_SHIFT_LSB |
824              1 << HSTX_CTRL_EXPAND_SHIFT_RAW_N_SHIFTS_LSB |
825              0 << HSTX_CTRL_EXPAND_SHIFT_RAW_SHIFT_LSB;
826          break;
827  
828      default:
829          dvhstx_debug("Unsupported mode %d", (int)mode);
830          return false;
831      }
832  
833      // Serial output config: clock period of 5 cycles, pop from command
834      // expander every 5 cycles, shift the output shiftreg by 2 every cycle.
835      hstx_ctrl_hw->csr = 0;
836      hstx_ctrl_hw->csr =
837          HSTX_CTRL_CSR_EXPAND_EN_BITS |
838          5u << HSTX_CTRL_CSR_CLKDIV_LSB |
839          5u << HSTX_CTRL_CSR_N_SHIFTS_LSB |
840          2u << HSTX_CTRL_CSR_SHIFT_LSB |
841          HSTX_CTRL_CSR_EN_BITS; 
842  
843      // HSTX outputs 0 through 7 appear on GPIO 12 through 19.
844      constexpr int HSTX_FIRST_PIN = 12;
845      // Assign clock pair to two neighbouring pins:
846      hstx_ctrl_hw->bit[(pinout.clk_p    ) - HSTX_FIRST_PIN] = HSTX_CTRL_BIT0_CLK_BITS;
847      hstx_ctrl_hw->bit[(pinout.clk_p ^ 1) - HSTX_FIRST_PIN] = HSTX_CTRL_BIT0_CLK_BITS | HSTX_CTRL_BIT0_INV_BITS;
848      for (uint lane = 0; lane < 3; ++lane) {
849          // For each TMDS lane, assign it to the correct GPIO pair based on the
850          // desired pinout:
851          int bit = pinout.rgb_p[lane];
852          // Output even bits during first half of each HSTX cycle, and odd bits
853          // during second half. The shifter advances by two bits each cycle.
854          uint32_t lane_data_sel_bits =
855              (lane * 10    ) << HSTX_CTRL_BIT0_SEL_P_LSB |
856              (lane * 10 + 1) << HSTX_CTRL_BIT0_SEL_N_LSB;
857          // The two halves of each pair get identical data, but one pin is inverted.
858          hstx_ctrl_hw->bit[(bit    ) - HSTX_FIRST_PIN] = lane_data_sel_bits;
859          hstx_ctrl_hw->bit[(bit ^ 1) - HSTX_FIRST_PIN] = lane_data_sel_bits | HSTX_CTRL_BIT0_INV_BITS;
860      }
861  
862      for (int i = 12; i <= 19; ++i) {
863          gpio_set_function(i, GPIO_FUNC_HSTX);
864          gpio_set_drive_strength(i, GPIO_DRIVE_STRENGTH_4MA);
865      }
866  
867      dvhstx_debug("GPIO configured\n");
868  
869      // The channels are set up identically, to transfer a whole scanline and
870      // then chain to the next channel. Each time a channel finishes, we
871      // reconfigure the one that just finished, meanwhile the other channel(s)
872      // are already making progress.
873      // Using just 2 channels was insufficient to avoid issues with the IRQ.
874      dma_channel_config c;
875      c = dma_channel_get_default_config(0);
876      channel_config_set_chain_to(&c, 1);
877      channel_config_set_dreq(&c, DREQ_HSTX);
878      dma_channel_configure(
879          0,
880          &c,
881          &hstx_fifo_hw->fifo,
882          vblank_line_vsync_off,
883          count_of(vblank_line_vsync_off),
884          false
885      );
886      c = dma_channel_get_default_config(1);
887      channel_config_set_chain_to(&c, 2);
888      channel_config_set_dreq(&c, DREQ_HSTX);
889      dma_channel_configure(
890          1,
891          &c,
892          &hstx_fifo_hw->fifo,
893          vblank_line_vsync_off,
894          count_of(vblank_line_vsync_off),
895          false
896      );
897      for (int i = 2; i < NUM_CHANS; ++i) {
898          c = dma_channel_get_default_config(i);
899          channel_config_set_chain_to(&c, (i+1) % NUM_CHANS);
900          channel_config_set_dreq(&c, DREQ_HSTX);
901          dma_channel_configure(
902              i,
903              &c,
904              &hstx_fifo_hw->fifo,
905              vblank_line_vsync_off,
906              count_of(vblank_line_vsync_off),
907              false
908          );
909      }
910  
911      dvhstx_debug("DMA channels claimed\n");
912  
913      dma_hw->intr = (1 << NUM_CHANS) - 1;
914      dma_hw->ints2 = (1 << NUM_CHANS) - 1;
915      dma_hw->inte2 = (1 << NUM_CHANS) - 1;
916      if (is_text_mode) irq_set_exclusive_handler(DMA_IRQ_2, dma_irq_handler_text);
917      else irq_set_exclusive_handler(DMA_IRQ_2, dma_irq_handler);
918      irq_set_enabled(DMA_IRQ_2, true);
919  
920      dma_channel_start(0);
921  
922      dvhstx_debug("DVHSTX started\n");
923  
924      inited = true;
925      return true;
926  }
927  
928  void DVHSTX::reset() {
929      if (!inited) return;
930      inited = false;
931  
932      hstx_ctrl_hw->csr = 0;
933  
934      irq_set_enabled(DMA_IRQ_2, false);
935      irq_remove_handler(DMA_IRQ_2, irq_get_exclusive_handler(DMA_IRQ_2));
936  
937      for (int i = 0; i < NUM_CHANS; ++i)
938          dma_channel_abort(i);
939  
940      if (font_cache) {
941          free(font_cache);
942          font_cache = nullptr;
943      }
944      free(line_buffers);
945      line_buffers = nullptr;
946  
947  #ifndef MICROPY_BUILD_TYPE
948      free(frame_buffer_display);
949      if (frame_buffer_display != frame_buffer_back) {
950          free(frame_buffer_back);
951  }
952      frame_buffer_display = frame_buffer_back = nullptr;
953  #endif
954  }
955  
956  void DVHSTX::flip_blocking() {
957      if (get_single_buffered())
958          return;
959      wait_for_vsync();
960      flip_now();
961  }
962  
963  void DVHSTX::flip_now() {
964      if (get_single_buffered())
965          return;
966      std::swap(frame_buffer_display, frame_buffer_back);
967  }
968  
969  void DVHSTX::wait_for_vsync() {
970      while (v_scanline >= timing_mode->v_front_porch) __wfe();
971  }
972  
973  void DVHSTX::flip_async() {
974      if (get_single_buffered())
975          return;
976      flip_next = true;
977  }
978  
979  void DVHSTX::wait_for_flip() {
980      if (get_single_buffered())
981          return;
982      while (flip_next) __wfe();
983  }