39
39
#include "sdk/src/rp2350/hardware_structs/include/hardware/structs/bus_ctrl.h"<
8000
/span>
40
40
#include "sdk/src/rp2350/hardware_structs/include/hardware/structs/hstx_ctrl.h"
41
41
#include "sdk/src/rp2350/hardware_structs/include/hardware/structs/hstx_fifo.h"
42
-
42
+ #include "sdk/src/rp2350/hardware_structs/include/hardware/structs/xip_ctrl.h"
43
43
// ----------------------------------------------------------------------------
44
44
// DVI constants
45
45
@@ -158,19 +158,82 @@ static uint32_t vactive_line720[VACTIVE_LEN] = {
158
158
HSTX_CMD_TMDS | MODE_720_H_ACTIVE_PIXELS
159
159
};
160
160
161
- picodvi_framebuffer_obj_t * active_picodvi = NULL ;
161
+ static picodvi_framebuffer_obj_t * _active_picodvi = NULL ;
162
162
163
- static void __not_in_flash_func (dma_irq_handler )(void ) {
164
- if (active_picodvi == NULL ) {
163
+ static void __not_in_flash_func (_dma_irq_handler )(void ) {
164
+ if (_active_picodvi == NULL || _active_picodvi -> dma_paused ) {
165
+ return ;
166
+ }
167
+ if (_active_picodvi -> dma_pausing ) {
168
+ // Disable command and pixel DMA channels to ensure no asynchronous transfers,
169
+ // triggers, or interrupts occur while paused.
170
+ dma_channel_hw_t * pix_ch = & dma_hw -> ch [_active_picodvi -> dma_pixel_channel ];
171
+ dma_channel_hw_t * cmd_ch = & dma_hw -> ch [_active_picodvi -> dma_command_channel ];
172
+ pix_ch -> al1_ctrl &= ~DMA_CH0_CTRL_TRIG_EN_BITS ;
173
+ cmd_ch -> al1_ctrl &= ~DMA_CH0_CTRL_TRIG_EN_BITS ;
174
+ _active_picodvi -> dma_pausing = false;
175
+ _active_picodvi -> dma_paused = true;
165
176
return ;
166
177
}
167
- uint ch_num = active_picodvi -> dma_pixel_channel ;
178
+ uint ch_num = _active_picodvi -> dma_pixel_channel ;
168
179
dma_hw -> intr = 1u << ch_num ;
169
180
170
181
// Set the read_addr back to the start and trigger the first transfer (which
171
182
// will trigger the pixel channel).
172
- dma_channel_hw_t * ch = & dma_hw -> ch [active_picodvi -> dma_command_channel ];
173
- ch -> al3_read_addr_trig = (uintptr_t )active_picodvi -> dma_commands ;
183
+ dma_channel_hw_t * ch = & dma_hw -> ch [_active_picodvi -> dma_command_channel ];
184
+ ch -> al3_read_addr_trig = (uintptr_t )_active_picodvi -> dma_commands ;
185
+ }
186
+
187
+ static void _pause_picodvi_dma (picodvi_framebuffer_obj_t * self , bool pause ) {
188
+ if (pause ) {
189
+ // Synchronize with end of the current frame.
190
+ // TODO: Presently we stop feeding the HSTX FIFO at the end of the current
191
+ // frame. Because the TMDS output stops when the HSTX FIFO empties, the monitor
192
+ // may lose synchronization leading to a blank screen for an extended period
193
+ // of time. As an alternative, consider switching to a fake framebuffer of
194
+ // all black pixels to maintain monitor synchronization.
195
+ self -> dma_pausing = true;
196
+ // Wait for the DMA IRQ handler to pause the DMA channels
197
+ while (!self -> dma_paused ) {
198
+ tight_loop_contents ();
199
+ }
200
+ assert (!self -> dma_pausing );
201
+ assert (self -> dma_paused );
202
+ assert (!dma_channel_is_busy (self -> dma_pixel_channel ));
203
+ assert (!dma_channel_is_busy (self -> dma_command_channel ));
204
+ } else {
205
+ self -> dma_paused = false;
206
+ _dma_irq_handler ();
207
+ }
208
+ }
209
+
210
+ static void _turn_off_dma (int channel ) {
211
+ if (channel < 0 ) {
212
+ return ;
213
+ }
214
+ dma_channel_config c = dma_channel_get_default_config (channel );
215
+ channel_config_set_enable (& c , false);
216
+ dma_channel_set_config (channel , & c , false /* trigger */ );
217
+
218
+ if (dma_channel_is_busy (channel )) {
219
+ dma_channel_abort (channel );
220
+ }
221
+ dma_channel_set_irq1_enabled (channel , false);
222
+ dma_channel_unclaim (channel );
223
+ }
224
+
225
+ void common_hal_picodvi_framebuffer_flash_pre_write () {
226
+ if (_active_picodvi == NULL || !_active_picodvi -> framebuffer_in_psram ) {
227
+ return ;
228
+ }
229
+ _pause_picodvi_dma (_active_picodvi , true);
230
+ }
231
+
232
+ void common_hal_picodvi_framebuffer_flash_post_write () {
233
+ if (_active_picodvi == NULL || !_active_picodvi -> framebuffer_in_psram ) {
234
+ return ;
235
+ }
236
+ _pause_picodvi_dma (_active_picodvi , false);
174
237
}
175
238
176
239
bool common_hal_picodvi_framebuffer_preflight (
@@ -215,7 +278,7 @@ void common_hal_picodvi_framebuffer_construct(picodvi_framebuffer_obj_t *self,
215
278
const mcu_pin_obj_t * green_dp , const mcu_pin_obj_t * green_dn ,
216
279
const mcu_pin_obj_t * blue_dp , const mcu_pin_obj_t * blue_dn ,
217
280
mp_uint_t color_depth ) {
218
- if (active_picodvi != NULL ) {
281
+ if (_active_picodvi != NULL ) {
219
282
mp_raise_msg_varg (& mp_type_RuntimeError , MP_ERROR_TEXT ("%q in use" ), MP_QSTR_picodvi );
220
283
}
221
284
@@ -267,10 +330,16 @@ void common_hal_picodvi_framebuffer_construct(picodvi_framebuffer_obj_t *self,
267
330
self -> pitch = (pitch_bytes + sizeof (uint32_t ) - 1 ) / sizeof (uint32_t );
268
331
size_t framebuffer_size = self -> pitch * self -> height ;
269
332
270
- // We check that allocations aren't in PSRAM because we haven't added XIP
271
- // streaming support.
272
- self -> framebuffer = (uint32_t * )port_malloc (framebuffer_size * sizeof (uint32_t ), true);
273
- if (self -> framebuffer == NULL || ((size_t )self -> framebuffer & 0xf0000000 ) == 0x10000000 ) {
333
+ // A framebuffer that exceeds the capacity of the SRAM will be allocated in PSRAM.
334
+ // If the framebuffer is in PSRAM, HSTX output will be paused during flash writes,
335
+ // resulting in a blank screen for the duration of the flash write.
336
+ //
337
+ // For testing purposes, add 1MB to the framebuffer allocation size to force it into PSRAM.
338
+ // NOTE: the dma_capable parameter of port_malloc() does nothing.
339
+ size_t framebuffer_alloc_size = framebuffer_size * sizeof (uint32_t ) + 1024 * 1024 ;
340
+ self -> framebuffer = (uint32_t * )port_malloc (framebuffer_alloc_size , true);
341
+ self -> framebuffer_in_psram = ((size_t )self -> framebuffer & 0xf0000000 ) == 0x10000000 ;
342
+ if (self -> framebuffer == NULL ) {
274
343
common_hal_picodvi_framebuffer_deinit (self );
275
344
m_malloc_fail (framebuffer_size * sizeof (uint32_t ));
276
345
return ;
@@ -563,34 +632,20 @@ void common_hal_picodvi_framebuffer_construct(picodvi_framebuffer_obj_t *self,
563
632
564
633
dma_hw -> ints1 = (1u << self -> dma_pixel_channel );
565
634
dma_hw -> inte1 = (1u << self -> dma_pixel_channel );
566
- irq_set_exclusive_handler (DMA_IRQ_1 , dma_irq_handler );
635
+ irq_set_exclusive_handler (DMA_IRQ_1 , _dma_irq_handler );
567
636
irq_set_enabled (DMA_IRQ_1 , true);
568
637
irq_set_priority (DMA_IRQ_1 , PICO_HIGHEST_IRQ_PRIORITY );
638
+ self -> dma_irq_handler_installed = true;
569
639
570
640
bus_ctrl_hw -> priority = BUSCTRL_BUS_PRIORITY_DMA_W_BITS | BUSCTRL_BUS_PRIORITY_DMA_R_BITS ;
571
641
572
642
// For the output.
573
643
self -> framebuffer_len = framebuffer_size ;
574
644
575
- active_picodvi = self ;
645
+ _active_picodvi = self ;
576
646
577
647
common_hal_picodvi_framebuffer_refresh (self );
578
- dma_irq_handler ();
579
- }
580
-
581
- static void _turn_off_dma (int channel ) {
582
- if (channel < 0 ) {
583
- return ;
584
- }
585
- d
10000
ma_channel_config c = dma_channel_get_default_config (channel );
586
- channel_config_set_enable (& c , false);
587
- dma_channel_set_config (channel , & c , false /* trigger */ );
588
-
589
- if (dma_channel_is_busy (channel )) {
590
- dma_channel_abort (channel );
591
- }
592
- dma_channel_set_irq1_enabled (channel , false);
593
- dma_channel_unclaim (channel );
648
+ _dma_irq_handler ();
594
649
}
595
650
596
651
void common_hal_picodvi_framebuffer_deinit (picodvi_framebuffer_obj_t * self ) {
@@ -607,7 +662,13 @@ void common_hal_picodvi_framebuffer_deinit(picodvi_framebuffer_obj_t *self) {
607
662
self -> dma_pixel_channel = -1 ;
608
663
self -> dma_command_channel = -1 ;
609
664
610
- active_picodvi = NULL ;
665
+ if (self -> dma_irq_handler_installed ) {
666
+ irq_set_enabled (DMA_IRQ_1 , false);
667
+ irq_remove_handler (DMA_IRQ_1 , _dma_irq_handler );
668
+ self -> dma_irq_handler_installed = false;
669
+ }
670
+
671
+ _active_picodvi = NULL ;
611
672
612
673
port_free (self -> framebuffer );
613
674
self -> framebuffer = NULL ;
0 commit comments