dvi_timing.c
1 #include "dvi.h" 2 #include "dvi_timing.h" 3 #include "hardware/dma.h" 4 5 // This file contains: 6 // - Timing parameters for DVI modes (horizontal + vertical counts, best 7 // achievable bit clock from 12 MHz crystal) 8 // - Helper functions for generating DMA lists based on these timings 9 10 // Pull into RAM but apply unique section suffix to allow linker GC 11 #define __dvi_func(x) __not_in_flash_func(x) 12 #define __dvi_const(x) __not_in_flash_func(x) 13 14 // VGA -- we do this mode properly, with a pretty comfortable clk_sys (252 MHz) 15 const struct dvi_timing __dvi_const(dvi_timing_640x480p_60hz) = { 16 .h_sync_polarity = false, 17 .h_front_porch = 16, 18 .h_sync_width = 96, 19 .h_back_porch = 48, 20 .h_active_pixels = 640, 21 22 .v_sync_polarity = false, 23 .v_front_porch = 10, 24 .v_sync_width = 2, 25 .v_back_porch = 33, 26 .v_active_lines = 480, 27 28 .bit_clk_khz = 252000 29 }; 30 31 // SVGA -- completely by-the-book but requires 400 MHz clk_sys 32 const struct dvi_timing __dvi_const(dvi_timing_800x600p_60hz) = { 33 .h_sync_polarity = false, 34 .h_front_porch = 44, 35 .h_sync_width = 128, 36 .h_back_porch = 88, 37 .h_active_pixels = 800, 38 39 .v_sync_polarity = false, 40 .v_front_porch = 1, 41 .v_sync_width = 4, 42 .v_back_porch = 23, 43 .v_active_lines = 600, 44 45 .bit_clk_khz = 400000 46 }; 47 48 // 800x480p 60 Hz (note this doesn't seem to be a CEA mode, I just used the 49 // output of `cvt 800 480 60`), 295 MHz bit clock 50 const struct dvi_timing __dvi_const(dvi_timing_800x480p_60hz) = { 51 .h_sync_polarity = false, 52 .h_front_porch = 24, 53 .h_sync_width = 72, 54 .h_back_porch = 96, 55 .h_active_pixels = 800, 56 57 .v_sync_polarity = true, 58 .v_front_porch = 3, 59 .v_sync_width = 10, 60 .v_back_porch = 7, 61 .v_active_lines = 480, 62 63 .bit_clk_khz = 295200 64 }; 65 66 // SVGA reduced blanking (355 MHz bit clock) -- valid CVT mode, less common 67 // than fully-blanked SVGA, but doesn't require such a high system clock 68 const struct dvi_timing __dvi_const(dvi_timing_800x600p_reduced_60hz) = { 69 .h_sync_polarity = true, 70 .h_front_porch = 48, 71 .h_sync_width = 32, 72 .h_back_porch = 80, 73 .h_active_pixels = 800, 74 75 .v_sync_polarity = false, 76 .v_front_porch = 3, 77 .v_sync_width = 4, 78 .v_back_porch = 11, 79 .v_active_lines = 600, 80 81 .bit_clk_khz = 354000 82 }; 83 84 // Also known as qHD, bit uncommon, but it's a nice modest-resolution 16:9 85 // aspect mode. Pixel clock 37.3 MHz 86 const struct dvi_timing __dvi_const(dvi_timing_960x540p_60hz) = { 87 .h_sync_polarity = true, 88 .h_front_porch = 16, 89 .h_sync_width = 32, 90 .h_back_porch = 96, 91 .h_active_pixels = 960, 92 93 .v_sync_polarity = true, 94 .v_front_porch = 2, 95 .v_sync_width = 6, 96 .v_back_porch = 15, 97 .v_active_lines = 540, 98 99 .bit_clk_khz = 372000 100 }; 101 102 // Note this is NOT the correct 720p30 CEA mode, but rather 720p60 run at half 103 // pixel clock. Seems to be commonly accepted (and is a valid CVT mode). The 104 // actual CEA mode is the same pixel clock as 720p60 but with >50% blanking, 105 // which would require a clk_sys of 742 MHz! 106 const struct dvi_timing __dvi_const(dvi_timing_1280x720p_30hz) = { 107 .h_sync_polarity = true, 108 .h_front_porch = 110, 109 .h_sync_width = 40, 110 .h_back_porch = 220, 111 .h_active_pixels = 1280, 112 113 .v_sync_polarity = true, 114 .v_front_porch = 5, 115 .v_sync_width = 5, 116 .v_back_porch = 20, 117 .v_active_lines = 720, 118 119 .bit_clk_khz = 372000 120 }; 121 122 // Reduced-blanking (CVT) 720p. You aren't supposed to use reduced blanking 123 // modes below 60 Hz, but I won't tell anyone (and it works on the monitors 124 // I've tried). This nets a lower system clock than regular 720p30 (319 MHz) 125 const struct dvi_timing __dvi_const(dvi_timing_1280x720p_reduced_30hz) = { 126 .h_sync_polarity = true, 127 .h_front_porch = 48, 128 .h_sync_width = 32, 129 .h_back_porch = 80, 130 .h_active_pixels = 1280, 131 132 .v_sync_polarity = false, 133 .v_front_porch = 3, 134 .v_sync_width = 5, 135 .v_back_porch = 13, 136 .v_active_lines = 720, 137 138 .bit_clk_khz = 319200 139 }; 140 141 // This requires a spicy 488 MHz system clock and is illegal in most countries 142 // (you need to have a very lucky piece of silicon to run this at 1.3 V, or 143 // connect an external supply and give it a bit more juice) 144 const struct dvi_timing __dvi_const(dvi_timing_1600x900p_reduced_30hz) = { 145 .h_sync_polarity = true, 146 .h_front_porch = 48, 147 .h_sync_width = 32, 148 .h_back_porch = 80, 149 .h_active_pixels = 1600, 150 151 .v_sync_polarity = false, 152 .v_front_porch = 3, 153 .v_sync_width = 5, 154 .v_back_porch = 18, 155 .v_active_lines = 900, 156 157 .bit_clk_khz = 488000 158 }; 159 160 // ---------------------------------------------------------------------------- 161 162 // The DMA scheme is: 163 // 164 // - One channel transferring data to each of the three PIO state machines 165 // performing TMDS serialisation 166 // 167 // - One channel programming the registers of each of these data channels, 168 // triggered (CHAIN_TO) each time the corresponding data channel completes 169 // 170 // - Lanes 1 and 2 have one block for blanking and one for video data 171 // 172 // - Lane 0 has one block for each horizontal region (front porch, hsync, back 173 // porch, active) 174 // 175 // - The IRQ_QUIET flag is used to select which data block on the sync lane is 176 // allowed to generate an IRQ upon completion. This is the block immediately 177 // before the horizontal active region. The IRQ is entered at ~the same time 178 // as the last data transfer starts 179 // 180 // - The IRQ points the control channels at new blocklists for next scanline. 181 // The DMA starts the new list automatically at end-of-scanline, via 182 // CHAIN_TO. 183 // 184 // The horizontal active region is the longest continuous transfer, so this 185 // gives the most time to handle the IRQ and load new blocklists. 186 // 187 // Note a null trigger IRQ is not suitable because we get that *after* the 188 // last data transfer finishes, and the FIFOs bottom out very shortly 189 // afterward. For pure DVI (four blocks per scanline), it works ok to take 190 // four regular IRQs per scanline and return early from 3 of them, but this 191 // breaks down when you have very short scanline sections like guard bands. 192 193 // Each symbol appears twice, concatenated in one word. Note these must be in 194 // RAM because they see a lot of DMA traffic 195 const uint32_t __dvi_const(dvi_ctrl_syms)[4] = { 196 0xd5354, 197 0x2acab, 198 0x55154, 199 0xaaeab 200 }; 201 202 // Output solid red scanline if we are given NULL for tmdsbuff 203 #if DVI_SYMBOLS_PER_WORD == 2 204 static uint32_t __dvi_const(empty_scanline_tmds)[3] = { 205 0x7fd00u, // 0x00, 0x00 206 0x7fd00u, // 0x00, 0x00 207 0xbfa01u // 0xfc, 0xfc 208 }; 209 #else 210 static uint32_t __attribute__((aligned(8))) __dvi_const(empty_scanline_tmds)[6] = { 211 0x100u, 0x1ffu, // 0x00, 0x00 212 0x100u, 0x1ffu, // 0x00, 0x00 213 0x201u, 0x2feu // 0xfc, 0xfc 214 }; 215 #endif 216 217 void dvi_timing_state_init(struct dvi_timing_state *t) { 218 t->v_ctr = 0; 219 t->v_state = DVI_STATE_FRONT_PORCH; 220 }; 221 222 void __dvi_func(dvi_timing_state_advance)(const struct dvi_timing *t, struct dvi_timing_state *s) { 223 s->v_ctr++; 224 if ((s->v_state == DVI_STATE_FRONT_PORCH && s->v_ctr == t->v_front_porch) || 225 (s->v_state == DVI_STATE_SYNC && s->v_ctr == t->v_sync_width) || 226 (s->v_state == DVI_STATE_BACK_PORCH && s->v_ctr == t->v_back_porch) || 227 (s->v_state == DVI_STATE_ACTIVE && s->v_ctr == t->v_active_lines)) { 228 229 s->v_state = (s->v_state + 1) % DVI_STATE_COUNT; 230 s->v_ctr = 0; 231 } 232 } 233 234 void dvi_scanline_dma_list_init(struct dvi_scanline_dma_list *dma_list) { 235 *dma_list = (struct dvi_scanline_dma_list){}; 236 } 237 238 static const uint32_t *get_ctrl_sym(bool vsync, bool hsync) { 239 return &dvi_ctrl_syms[!!vsync << 1 | !!hsync]; 240 } 241 242 // Make a sequence of paced transfers to the relevant FIFO 243 static void _set_data_cb(dma_cb_t *cb, const struct dvi_lane_dma_cfg *dma_cfg, 244 const void *read_addr, uint transfer_count, uint read_ring, bool irq_on_finish) { 245 cb->read_addr = read_addr; 246 cb->write_addr = dma_cfg->tx_fifo; 247 cb->transfer_count = transfer_count; 248 cb->c = dma_channel_get_default_config(dma_cfg->chan_data); 249 channel_config_set_ring(&cb->c, false, read_ring); 250 channel_config_set_dreq(&cb->c, dma_cfg->dreq); 251 // Call back to control channel for reconfiguration: 252 channel_config_set_chain_to(&cb->c, dma_cfg->chan_ctrl); 253 // Note we never send a null trigger, so IRQ_QUIET is an IRQ suppression flag 254 channel_config_set_irq_quiet(&cb->c, !irq_on_finish); 255 }; 256 257 void dvi_setup_scanline_for_vblank(const struct dvi_timing *t, const struct dvi_lane_dma_cfg dma_cfg[], 258 bool vsync_asserted, struct dvi_scanline_dma_list *l) { 259 260 bool vsync = t->v_sync_polarity == vsync_asserted; 261 const uint32_t *sym_hsync_off = get_ctrl_sym(vsync, !t->h_sync_polarity); 262 const uint32_t *sym_hsync_on = get_ctrl_sym(vsync, t->h_sync_polarity); 263 const uint32_t *sym_no_sync = get_ctrl_sym(false, false ); 264 265 dma_cb_t *synclist = dvi_lane_from_list(l, TMDS_SYNC_LANE); 266 // The symbol table contains each control symbol *twice*, concatenated into 20 LSBs of table word, so we can always do word-repeat. 267 _set_data_cb(&synclist[0], &dma_cfg[TMDS_SYNC_LANE], sym_hsync_off, t->h_front_porch / DVI_SYMBOLS_PER_WORD, 2, false); 268 _set_data_cb(&synclist[1], &dma_cfg[TMDS_SYNC_LANE], sym_hsync_on, t->h_sync_width / DVI_SYMBOLS_PER_WORD, 2, false); 269 _set_data_cb(&synclist[2], &dma_cfg[TMDS_SYNC_LANE], sym_hsync_off, t->h_back_porch / DVI_SYMBOLS_PER_WORD, 2, true); 270 _set_data_cb(&synclist[3], &dma_cfg[TMDS_SYNC_LANE], sym_hsync_off, t->h_active_pixels / DVI_SYMBOLS_PER_WORD, 2, false); 271 272 for (int i = 0; i < N_TMDS_LANES; ++i) { 273 if (i == TMDS_SYNC_LANE) 274 continue; 275 dma_cb_t *cblist = dvi_lane_from_list(l, i); 276 _set_data_cb(&cblist[0], &dma_cfg[i], sym_no_sync,(t->h_front_porch + t->h_sync_width + t->h_back_porch) / DVI_SYMBOLS_PER_WORD, 2, false); 277 _set_data_cb(&cblist[1], &dma_cfg[i], sym_no_sync, t->h_active_pixels / DVI_SYMBOLS_PER_WORD, 2, false); 278 } 279 } 280 281 void dvi_setup_scanline_for_active(const struct dvi_timing *t, const struct dvi_lane_dma_cfg dma_cfg[], 282 uint32_t *tmdsbuf, struct dvi_scanline_dma_list *l) { 283 284 const uint32_t *sym_hsync_off = get_ctrl_sym(!t->v_sync_polarity, !t->h_sync_polarity); 285 const uint32_t *sym_hsync_on = get_ctrl_sym(!t->v_sync_polarity, t->h_sync_polarity); 286 const uint32_t *sym_no_sync = get_ctrl_sym(false, false ); 287 288 dma_cb_t *synclist = dvi_lane_from_list(l, TMDS_SYNC_LANE); 289 _set_data_cb(&synclist[0], &dma_cfg[TMDS_SYNC_LANE], sym_hsync_off, t->h_front_porch / DVI_SYMBOLS_PER_WORD, 2, false); 290 _set_data_cb(&synclist[1], &dma_cfg[TMDS_SYNC_LANE], sym_hsync_on, t->h_sync_width / DVI_SYMBOLS_PER_WORD, 2, false); 291 _set_data_cb(&synclist[2], &dma_cfg[TMDS_SYNC_LANE], sym_hsync_off, t->h_back_porch / DVI_SYMBOLS_PER_WORD, 2, true); 292 293 for (int i = 0; i < N_TMDS_LANES; ++i) { 294 dma_cb_t *cblist = dvi_lane_from_list(l, i); 295 if (i != TMDS_SYNC_LANE) { 296 _set_data_cb(&cblist[0], &dma_cfg[i], sym_no_sync, 297 (t->h_front_porch + t->h_sync_width + t->h_back_porch) / DVI_SYMBOLS_PER_WORD, 2, false); 298 } 299 int target_block = i == TMDS_SYNC_LANE ? DVI_SYNC_LANE_CHUNKS - 1 : DVI_NOSYNC_LANE_CHUNKS - 1; 300 if (tmdsbuf) { 301 // Non-repeating DMA for the freshly-encoded TMDS buffer 302 _set_data_cb(&cblist[target_block], &dma_cfg[i], tmdsbuf + i * (t->h_active_pixels / DVI_SYMBOLS_PER_WORD), 303 t->h_active_pixels / DVI_SYMBOLS_PER_WORD, 0, false); 304 } 305 else { 306 // Use read ring to repeat the correct DC-balanced symbol pair on blank scanlines (4 or 8 byte period) 307 _set_data_cb(&cblist[target_block], &dma_cfg[i], &empty_scanline_tmds[2 * i / DVI_SYMBOLS_PER_WORD], 308 t->h_active_pixels / DVI_SYMBOLS_PER_WORD, DVI_SYMBOLS_PER_WORD == 2 ? 2 : 3, false); 309 } 310 } 311 } 312 313 void __dvi_func(dvi_update_scanline_data_dma)(const struct dvi_timing *t, const uint32_t *tmdsbuf, struct dvi_scanline_dma_list *l) { 314 for (int i = 0; i < N_TMDS_LANES; ++i) { 315 #if DVI_MONOCHROME_TMDS 316 const uint32_t *lane_tmdsbuf = tmdsbuf; 317 #else 318 const uint32_t *lane_tmdsbuf = tmdsbuf + i * t->h_active_pixels / DVI_SYMBOLS_PER_WORD; 319 #endif 320 if (i == TMDS_SYNC_LANE) 321 dvi_lane_from_list(l, i)[3].read_addr = lane_tmdsbuf; 322 else 323 dvi_lane_from_list(l, i)[1].read_addr = lane_tmdsbuf; 324 } 325 } 326