Skip to content

Commit 4e519a9

Browse files
authored
Merge pull request #10116 from eightycc/framebuffer2
Fixes to RP2350 Framebuffer and handling of flash writes
2 parents 2613ceb + e85f710 commit 4e519a9

File tree

6 files changed

+51
-80
lines changed

6 files changed

+51
-80
lines changed

ports/raspberrypi/bindings/picodvi/Framebuffer.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,11 @@
5151
//| The framebuffer pixel format varies depending on color_depth:
5252
//|
5353
//| * 1 - Each bit is a pixel. Either white (1) or black (0).
54-
//| * 2 - Each 2 bits is a pixels. Grayscale between white (0x3) and black (0x0).
55-
//| * 4 - Each nibble is a pixels in RGB format. The fourth bit is ignored. (RP2350 only)
56-
//| * 8 - Each byte is a pixels in RGB332 format.
57-
//| * 16 - Each two bytes are a pixel in RGB565 format.
58-
//| * 32 - Each four bytes are a pixel in RGB888 format. The top byte is ignored.
54+
//| * 2 - Each two bits is a pixel. Grayscale between white (0x3) and black (0x0).
55+
//| * 4 - Each nibble is a pixel in RGB format. The fourth bit is ignored. (RP2350 only)
56+
//| * 8 - Each byte is a pixel in RGB332 format.
57+
//| * 16 - Each two bytes is a pixel in RGB565 format.
58+
//| * 32 - Each four bytes is a pixel in RGB888 format. The top byte is ignored.
5959
//|
6060
//| Output resolution support varies between the RP2040 and RP2350.
6161
//|

ports/raspberrypi/common-hal/microcontroller/__init__.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ void common_hal_mcu_enable_interrupts(void) {
5555
#define PICO_ELEVATED_IRQ_PRIORITY (0x60) // between PICO_DEFAULT and PIOCO_HIGHEST_IRQ_PRIORITY
5656
static uint32_t oldBasePri = 0; // 0 (default) masks nothing, other values mask equal-or-larger priority values
5757
void common_hal_mcu_disable_interrupts(void) {
58+
uint32_t my_interrupts = save_and_disable_interrupts();
5859
if (nesting_count == 0) {
5960
// We must keep DMA_IRQ_1 (reserved for pico dvi) enabled at all times,
6061
// including during flash writes. Do this by setting the priority mask (BASEPRI
@@ -66,6 +67,7 @@ void common_hal_mcu_disable_interrupts(void) {
6667
__isb(); // Instruction synchronization barrier
6768
}
6869
nesting_count++;
70+
restore_interrupts(my_interrupts);
6971
}
7072

7173
void common_hal_mcu_enable_interrupts(void) {

ports/raspberrypi/common-hal/nvm/ByteArray.c

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,16 @@ static void write_page(uint32_t page_addr, uint32_t offset, uint32_t len, uint8_
2727
// Write a whole page to flash, buffering it first and then erasing and rewriting it
2828
// since we can only write a whole page at a time.
2929
if (offset == 0 && len == FLASH_PAGE_SIZE) {
30-
// disable interrupts to prevent core hang on rp2040
31-
common_hal_mcu_disable_interrupts();
3230
supervisor_flash_pre_write();
3331
flash_range_program(RMV_OFFSET(page_addr), bytes, FLASH_PAGE_SIZE);
3432
supervisor_flash_post_write();
35-
common_hal_mcu_enable_interrupts();
3633
} else {
3734
uint8_t buffer[FLASH_PAGE_SIZE];
3835
memcpy(buffer, (uint8_t *)page_addr, FLASH_PAGE_SIZE);
3936
memcpy(buffer + offset, bytes, len);
40-
common_hal_mcu_disable_interrupts();
4137
supervisor_flash_pre_write();
4238
flash_range_program(RMV_OFFSET(page_addr), buffer, FLASH_PAGE_SIZE);
4339
supervisor_flash_post_write();
44-
common_hal_mcu_enable_interrupts();
4540
}
4641

4742
}

ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,11 @@ static void __not_in_flash_func(dma_irq_handler)(void) {
165165
return;
166166
}
167167
uint ch_num = active_picodvi->dma_pixel_channel;
168-
dma_channel_hw_t *ch = &dma_hw->ch[ch_num];
169168
dma_hw->intr = 1u << ch_num;
170169

171170
// Set the read_addr back to the start and trigger the first transfer (which
172171
// will trigger the pixel channel).
173-
ch = &dma_hw->ch[active_picodvi->dma_command_channel];
172+
dma_channel_hw_t *ch = &dma_hw->ch[active_picodvi->dma_command_channel];
174173
ch->al3_read_addr_trig = (uintptr_t)active_picodvi->dma_commands;
175174
}
176175

@@ -224,6 +223,9 @@ void common_hal_picodvi_framebuffer_construct(picodvi_framebuffer_obj_t *self,
224223
mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q and %q"), MP_QSTR_width, MP_QSTR_height);
225224
}
226225

226+
self->dma_command_channel = -1;
227+
self->dma_pixel_channel = -1;
228+
227229
if (width % 160 == 0) {
228230
self->output_width = 640;
229231
} else {
@@ -258,24 +260,18 @@ void common_hal_picodvi_framebuffer_construct(picodvi_framebuffer_obj_t *self,
258260

259261
self->width = width;
260262
self->height = height;
261-
self->pitch = (self->width * color_depth) / 8;
262263
self->color_depth = color_depth;
263-
// Align each row to words.
264-
if (self->pitch % sizeof(uint32_t) != 0) {
265-
self->pitch += sizeof(uint32_t) - (self->pitch % sizeof(uint32_t));
266-
}
267-
self->pitch /= sizeof(uint32_t);
264+
// Pitch is number of 32-bit words per line. We round up pitch_bytes to the nearest word
265+
// so that each scanline begins on a natural 32-bit word boundary.
266+
size_t pitch_bytes = (self->width * color_depth) / 8;
267+
self->pitch = (pitch_bytes + sizeof(uint32_t) - 1) / sizeof(uint32_t);
268268
size_t framebuffer_size = self->pitch * self->height;
269269

270270
// We check that allocations aren't in PSRAM because we haven't added XIP
271271
// streaming support.
272272
self->framebuffer = (uint32_t *)port_malloc(framebuffer_size * sizeof(uint32_t), true);
273273
if (self->framebuffer == NULL || ((size_t)self->framebuffer & 0xf0000000) == 0x10000000) {
274-
if (self->framebuffer != NULL) {
275-
// Return the memory in PSRAM.
276-
port_free(self->framebuffer);
277-
self->framebuffer = NULL;
278-
}
274+
common_hal_picodvi_framebuffer_deinit(self);
279275
m_malloc_fail(framebuffer_size * sizeof(uint32_t));
280276
return;
281277
}
@@ -296,26 +292,26 @@ void common_hal_picodvi_framebuffer_construct(picodvi_framebuffer_obj_t *self,
296292
self->dma_commands_len = (MODE_720_V_FRONT_PORCH + MODE_720_V_SYNC_WIDTH + MODE_720_V_BACK_PORCH + 2 * MODE_720_V_ACTIVE_LINES + 1) * dma_command_size;
297293
}
298294
self->dma_commands = (uint32_t *)port_malloc(self->dma_commands_len * sizeof(uint32_t), true);
299-
if (self->dma_commands == NULL || ((size_t)self->framebuffer & 0xf0000000) == 0x10000000) {
300-
port_free(self->framebuffer);
295+
if (self->dma_commands == NULL || ((size_t)self->dma_commands & 0xf0000000) == 0x10000000) {
296+
common_hal_picodvi_framebuffer_deinit(self);
301297
m_malloc_fail(self->dma_commands_len * sizeof(uint32_t));
302298
return;
303299
}
304300

305-
int dma_pixel_channel_maybe = dma_claim_unused_channel(false);
306-
if (dma_pixel_channel_maybe < 0) {
307-
mp_raise_RuntimeError(MP_ERROR_TEXT("Internal resource(s) in use"));
308-
return;
309-
}
301+
// The command channel and the pixel channel form a pipeline that feeds combined HSTX
302+
// commands and pixel data to the HSTX FIFO. The command channel reads a pre-computed
303+
// list of control/status words from the dma_commands buffer and writes them to the
304+
// pixel channel's control/status registers. Under control of the command channel, the
305+
// pixel channel smears/swizzles pixel data from the framebuffer and combines
306+
// it with HSTX commands, forwarding the combined stream to the HSTX FIFO.
310307

311-
int dma_command_channel_maybe = dma_claim_unused_channel(false);
312-
if (dma_command_channel_maybe < 0) {
313-
dma_channel_unclaim((uint)dma_pixel_channel_maybe);
308+
self->dma_pixel_channel = dma_claim_unused_channel(false);
309+
self->dma_command_channel = dma_claim_unused_channel(false);
310+
if (self->dma_pixel_channel < 0 || self->dma_command_channel < 0) {
311+
common_hal_picodvi_framebuffer_deinit(self);
314312
mp_raise_RuntimeError(MP_ERROR_TEXT("Internal resource(s) in use"));
315313
return;
316314
}
317-
self->dma_pixel_channel = dma_pixel_channel_maybe;
318-
self->dma_command_channel = dma_command_channel_maybe;
319315

320316
size_t command_word = 0;
321317
size_t frontporch_start;
@@ -582,7 +578,10 @@ void common_hal_picodvi_framebuffer_construct(picodvi_framebuffer_obj_t *self,
582578
dma_irq_handler();
583579
}
584580

585-
static void _turn_off_dma(uint8_t channel) {
581+
static void _turn_off_dma(int channel) {
582+
if (channel < 0) {
583+
return;
584+
}
586585
dma_channel_config c = dma_channel_get_default_config(channel);
587586
channel_config_set_enable(&c, false);
588587
dma_channel_set_config(channel, &c, false /* trigger */);
@@ -605,6 +604,8 @@ void common_hal_picodvi_framebuffer_deinit(picodvi_framebuffer_obj_t *self) {
605604

606605
_turn_off_dma(self->dma_pixel_channel);
607606
_turn_off_dma(self->dma_command_channel);
607+
self->dma_pixel_channel = -1;
608+
self->dma_command_channel = -1;
608609

609610
active_picodvi = NULL;
610611

ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,6 @@ typedef struct {
3939
mp_uint_t output_width;
4040
uint16_t pitch; // Number of words between rows. (May be more than a width's worth.)
4141
uint8_t color_depth;
42-
uint8_t dma_pixel_channel;
43-
uint8_t dma_command_channel;
42+
int dma_pixel_channel;
43+
int dma_command_channel;
4444
} picodvi_framebuffer_obj_t;

ports/raspberrypi/supervisor/internal_flash.c

Lines changed: 15 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -40,41 +40,26 @@
4040
static uint8_t _cache[SECTOR_SIZE];
4141
static uint32_t _cache_lba = NO_CACHE;
4242
static uint32_t _flash_size = 0;
43-
44-
#ifdef PICO_RP2350
45-
static uint32_t m1_rfmt;
46-
static uint32_t m1_timing;
43+
#if CIRCUITPY_AUDIOCORE
44+
static uint32_t _audio_channel_mask;
4745
#endif
4846

49-
static void __no_inline_not_in_flash_func(save_psram_settings)(void) {
50-
#ifdef PICO_RP2350
51-
// We're about to invalidate the XIP cache, clean it first to commit any dirty writes to PSRAM
52-
// From https://forums.raspberrypi.com/viewtopic.php?t=378249#p2263677
53-
// Perform clean-by-set/way on all lines
54-
for (uint32_t i = 0; i < 2048; ++i) {
55-
// Use the upper 16k of the maintenance space (0x1bffc000 through 0x1bffffff):
56-
*(volatile uint8_t *)(XIP_SRAM_BASE + (XIP_MAINTENANCE_BASE - XIP_BASE) + i * 8u + 0x1u) = 0;
57-
}
58-
59-
m1_timing = qmi_hw->m[1].timing;
60-
m1_rfmt = qmi_hw->m[1].rfmt;
61-
#endif
62-
}
63-
64-
static void __no_inline_not_in_flash_func(restore_psram_settings)(void) {
65-
#ifdef PICO_RP2350
66-
qmi_hw->m[1].timing = m1_timing;
67-
qmi_hw->m[1].rfmt = m1_rfmt;
68-
__compiler_memory_barrier();
69-
#endif
70-
}
71-
7247
void supervisor_flash_pre_write(void) {
73-
save_psram_settings();
48+
// Disable interrupts. XIP accesses will fault during flash writes.
49+
common_hal_mcu_disable_interrupts();
50+
#if CIRCUITPY_AUDIOCORE
51+
// Pause audio DMA to avoid noise while interrupts are disabled.
52+
_audio_channel_mask = audio_dma_pause_all();
53+
#endif
7454
}
7555

7656
void supervisor_flash_post_write(void) {
77-
restore_psram_settings();
57+
#if CIRCUITPY_AUDIOCORE
58+
// Unpause audio DMA.
59+
audio_dma_unpause_mask(_audio_channel_mask);
60+
#endif
61+
// Re-enable interrupts.
62+
common_hal_mcu_enable_interrupts();
7863
}
7964

8065
void supervisor_flash_init(void) {
@@ -91,11 +76,9 @@ void supervisor_flash_init(void) {
9176
// Read the RDID register to get the flash capacity.
9277
uint8_t cmd[] = {0x9f, 0, 0, 0};
9378
uint8_t data[4];
94-
common_hal_mcu_disable_interrupts();
9579
supervisor_flash_pre_write();
9680
flash_do_cmd(cmd, data, 4);
9781
supervisor_flash_post_write();
98-
common_hal_mcu_enable_interrupts();
9982
uint8_t power_of_two = FLASH_DEFAULT_POWER_OF_TWO;
10083
// Flash must be at least 2MB (1 << 21) because we use the first 1MB for the
10184
// CircuitPython core. We validate the range because Adesto Tech flash chips
@@ -119,21 +102,11 @@ void port_internal_flash_flush(void) {
119102
if (_cache_lba == NO_CACHE) {
120103
return;
121104
}
122-
// Make sure we don't have an interrupt while we do flash operations.
123-
common_hal_mcu_disable_interrupts();
124-
// and audio DMA must be paused as well
125-
#if CIRCUITPY_AUDIOCORE
126-
uint32_t channel_mask = audio_dma_pause_all();
127-
#endif
128105
supervisor_flash_pre_write();
129106
flash_range_erase(CIRCUITPY_CIRCUITPY_DRIVE_START_ADDR + _cache_lba, SECTOR_SIZE);
130107
flash_range_program(CIRCUITPY_CIRCUITPY_DRIVE_START_ADDR + _cache_lba, _cache, SECTOR_SIZE);
131-
supervisor_flash_post_write();
132108
_cache_lba = NO_CACHE;
133-
#if CIRCUITPY_AUDIOCORE
134-
audio_dma_unpause_mask(channel_mask);
135-
#endif
136-
common_hal_mcu_enable_interrupts();
109+
supervisor_flash_post_write();
137110
}
138111

139112
mp_uint_t supervisor_flash_read_blocks(uint8_t *dest, uint32_t block, uint32_t num_blocks) {

0 commit comments

Comments
 (0)