diff --git a/ports/broadcom/bindings/videocore/Hvs.c b/ports/broadcom/bindings/videocore/Hvs.c index 645dce43c1..36468ef57e 100644 --- a/ports/broadcom/bindings/videocore/Hvs.c +++ b/ports/broadcom/bindings/videocore/Hvs.c @@ -6,7 +6,11 @@ #include "bindings/videocore/Sprite.h" #include "shared-bindings/displayio/Bitmap.h" +#include "shared-module/displayio/Group.h" +#include "shared-bindings/displayio/Group.h" #include "py/runtime.h" +#include "shared-bindings/displayio/TileGrid.h" +#include "shared-bindings/displayio/Palette.h" #include "bindings/videocore/hvs.h" #include "bindings/videocore/Hvs.h" @@ -17,7 +21,6 @@ volatile uint32_t* dlist_memory = (volatile uint32_t*)SCALER5_LIST_MEMORY; volatile uint32_t* dlist_memory = (volatile uint32_t*)SCALER_LIST_MEMORY; #endif -extern const mp_obj_type_t hvs_channel_type; volatile struct hvs_channel *hvs_hw_channels = (volatile struct hvs_channel*)SCALER_DISPCTRL0; uint32_t dlist_slot = 128; // start a bit in, to not trash the firmware list @@ -37,12 +40,21 @@ hvs_channel_t hvs_channels[3] = { }; static mp_obj_t c_set_sprite_list(mp_obj_t self_in, mp_obj_t list) { + mp_obj_t *unique_palettes = mp_obj_new_set(0, NULL); hvs_channel_t *self = MP_OBJ_TO_PTR(self_in); mp_obj_t sprite_list = mp_arg_validate_type(list, &mp_type_list, MP_QSTR_sprites); size_t len = 0; mp_obj_t *items; mp_obj_list_get(sprite_list, &len, &items); + // first loop, find unique palettes + for (uint32_t i=0; ipalette) mp_obj_set_store(unique_palettes, s->palette); + } + // TODO, copy each palette to the dlist memory and set sprite->palette_addr to the addr its copied to uint32_t needed_slots = 1; // one more, to terminate the list + // second loop, regenerate any display lists and count the total size for (uint32_t i=0; i 4096) { - // early loop) dlist_slot = 128; } uint32_t starting_slot = dlist_slot; + // third loop, copy dlist fragments to hw for (uint32_t i=0; ichannel); + printf("root group x: %d y: %d\n", root_group->x, root_group->y); + printf("group scale (not implemented): %d\n", root_group->scale); + size_t len = 0; + mp_obj_t *items; + mp_obj_list_get(root_group->members, &len, &items); + printf("%d elements\n", len); + for (uint i=0; ibitmap, &displayio_bitmap_type, MP_QSTR_bitmap); + uint8_t *arr; + if (tg->inline_tiles) arr = (uint8_t*)&tg->tiles; + else arr = tg->tiles; + /* + printf("found tilegrid: %p\n", tg); + printf("x %d, y %d\n", tg->x, tg->y); + printf("w %d, h %d\n", tg->width_in_tiles, tg->height_in_tiles); + printf("w %d, h %d\n", tg->pixel_width, tg->pixel_height); + printf("w %d, h %d\n", tg->tile_width, tg->tile_height); + printf("shader type: %p\n", mp_obj_get_type(tg->pixel_shader)); + */ + enum hvs_pixel_format format = HVS_PIXEL_FORMAT_RGB332; + int order = HVS_PIXEL_ORDER_ABGR; + if (mp_obj_get_type(tg->pixel_shader) == &displayio_palette_type) { + puts("tg uses a palette"); + format = HVS_PIXEL_FORMAT_PALETTE; + } + for (uint row=0; row < tg->height_in_tiles; row++) { + for (uint col=0; col < tg->width_in_tiles; col++) { + uint tile_offset = col + (row * tg->width_in_tiles); + sprite_t *sprite = mp_obj_malloc(sprite_t, &hvs_sprite_type); + sprite->bitmap = bitmap; + if (format == HVS_PIXEL_FORMAT_PALETTE) sprite->palette = tg->pixel_shader; + else sprite->palette = NULL; + sprite->x = root_group->x + tg->x + (col * tg->tile_width); + sprite->y = root_group->y + tg->y + (row * tg->tile_height); + uint tile = arr[tile_offset]; + sprite->x_offset = (tile % tg->bitmap_width_in_tiles) * tg->tile_width; + sprite->y_offset = (tile / tg->bitmap_width_in_tiles) * tg->tile_height; + sprite->width = tg->tile_width; + sprite->height = tg->tile_height; + sprite->dirty = true; + sprite->color_order = order; + sprite->pixel_format = format; + mp_obj_list_append(sprites, sprite); + } + } + } else { + puts("unexpected item in group"); + } + } + c_set_sprite_list(self_in, sprites); + return mp_const_none; +} + MP_DEFINE_CONST_FUN_OBJ_2(fun_set_sprite_list, c_set_sprite_list); +MP_DEFINE_CONST_FUN_OBJ_2(fun_hvs_show, c_hvs_show); #define simpleprop(name) \ static MP_DEFINE_CONST_FUN_OBJ_1(fun_get_##name, c_getter_##name); \ @@ -104,15 +180,32 @@ static mp_obj_t c_getter_enabled(mp_obj_t self_in) { return mp_obj_new_bool(ctrl & SCALER_DISPCTRLX_ENABLE); } +static mp_obj_t c_getter_frame(mp_obj_t self_in) { + hvs_channel_t *self = MP_OBJ_TO_PTR(self_in); + uint32_t stat = hvs_hw_channels[self->channel].dispstat; + return MP_OBJ_NEW_SMALL_INT((stat >> 12) & 0x3f); +} + +static mp_obj_t c_getter_scanline(mp_obj_t self_in) { + hvs_channel_t *self = MP_OBJ_TO_PTR(self_in); + uint32_t stat = hvs_hw_channels[self->channel].dispstat; + return MP_OBJ_NEW_SMALL_INT(stat & 0xfff); +} + simpleprop(width); simpleprop(height); simpleprop(enabled); +simpleprop(frame); +simpleprop(scanline); static const mp_rom_map_elem_t hvs_channel_locals_dict_table[] = { prop_entry(width), prop_entry(height), prop_entry(enabled), + prop_entry(frame), + prop_entry(scanline), { MP_ROM_QSTR(MP_QSTR_set_sprite_list), MP_ROM_PTR(&fun_set_sprite_list) }, + { MP_ROM_QSTR(MP_QSTR_show), MP_ROM_PTR(&fun_hvs_show) }, }; static MP_DEFINE_CONST_DICT(hvs_channel_locals_dict, hvs_channel_locals_dict_table); diff --git a/ports/broadcom/bindings/videocore/Hvs.h b/ports/broadcom/bindings/videocore/Hvs.h index 6df601791d..e0f4df8a21 100644 --- a/ports/broadcom/bindings/videocore/Hvs.h +++ b/ports/broadcom/bindings/videocore/Hvs.h @@ -6,3 +6,4 @@ typedef struct { } hvs_channel_t; extern hvs_channel_t hvs_channels[3]; +extern const mp_obj_type_t hvs_channel_type; diff --git a/ports/broadcom/bindings/videocore/Sprite.c b/ports/broadcom/bindings/videocore/Sprite.c index 071a375502..05795ee327 100644 --- a/ports/broadcom/bindings/videocore/Sprite.c +++ b/ports/broadcom/bindings/videocore/Sprite.c @@ -31,27 +31,58 @@ enum hvs_pixel_format bitmap_to_hvs(const displayio_bitmap_t *bitmap) { #error not implemented yet #else void hvs_regen_noscale_noviewport(sprite_t *s) { + printf("xoffset: %ld yoffset: %ld\n", s->x_offset, s->y_offset); uint32_t *d = s->dlist; + uint32_t phys_addr = (uint32_t)s->bitmap->data; // assumes identity map, should be physical addr + int bytes_per_pixel = s->bitmap->bits_per_value / 8; + uint stride = s->bitmap->stride * 4; + phys_addr += bytes_per_pixel * s->x_offset; + phys_addr += stride * s->y_offset; + uint size = 7; + if (s->pixel_format == HVS_PIXEL_FORMAT_PALETTE) size = 8; // CTL0 d[0] = CONTROL_VALID | CONTROL_PIXEL_ORDER(s->color_order) | CONTROL_UNITY | CONTROL_FORMAT(s->pixel_format) - | CONTROL_WORDS(7); + | CONTROL_WORDS(size); // POS0 d[1] = POS0_X(s->x) | POS0_Y(s->y) | POS0_ALPHA(0xff); // POS2, input size - d[2] = POS2_H(s->bitmap->height) | POS2_W(s->bitmap->width) | (s->alpha_mode << 30); + d[2] = POS2_H(s->height) | POS2_W(s->width) | (s->alpha_mode << 30); // POS3, context d[3] = 0xDEADBEEF; // PTR0 - d[4] = ((uint32_t)s->bitmap->data) // assumes identity map, should be physical addr + d[4] = phys_addr | 0xc0000000; // and tell HVS to do uncached reads // context 0 d[5] = 0xDEADBEEF; // pitch 0 - d[6] = s->bitmap->stride * 4; - + d[6] = stride; + if (s->pixel_format == HVS_PIXEL_FORMAT_PALETTE) { + // https://github.com/librerpi/rpi-open-firmware/blob/93dc394d33f54a59a25a087213f46a20f5aad327/docs/hvs.txt#L107-L112 + uint bitsize; + switch (s->bitmap->bits_per_value) { + case 1: + bitsize = 0; + break; + case 2: + bitsize = 1; + break; + case 4: + bitsize = 2; + break; + case 8: + bitsize = 3; + break; + default: + d[0] = 0; + return; + } + uint initial_pixel_offset = 0; + uint order = 0; + d[7] = (bitsize << 30) | (initial_pixel_offset << 27) | (order << 26) | (s->palette_addr << 2); + } //printf("w: %d, h: %d, stride: %d, bits per value: %d\n", s->bitmap->width, s->bitmap->height, s->bitmap->stride, s->bitmap->bits_per_value); } #endif diff --git a/ports/broadcom/bindings/videocore/Sprite.h b/ports/broadcom/bindings/videocore/Sprite.h index f28fe7991a..67c26233d0 100644 --- a/ports/broadcom/bindings/videocore/Sprite.h +++ b/ports/broadcom/bindings/videocore/Sprite.h @@ -11,10 +11,14 @@ typedef struct { uint32_t height; uint32_t x; uint32_t y; + uint32_t x_offset; + uint32_t y_offset; uint32_t dlist[32]; enum alpha_mode alpha_mode; uint32_t color_order; + uint32_t palette_addr; enum hvs_pixel_format pixel_format; + mp_obj_t palette; } sprite_t; extern const mp_obj_type_t hvs_sprite_type; diff --git a/ports/broadcom/bindings/videocore/code.py b/ports/broadcom/bindings/videocore/code.py index 05614c9498..a7ae257f28 100755 --- a/ports/broadcom/bindings/videocore/code.py +++ b/ports/broadcom/bindings/videocore/code.py @@ -23,9 +23,19 @@ def gif_animate(): x = 5 y = 5 + lastframe = videocore.HvsChannel1.frame + start = time.monotonic() while True: + framedelta = videocore.HvsChannel1.frame - lastframe + lastframe = videocore.HvsChannel1.frame + #print(f"delta: {framedelta} scanline: {videocore.HvsChannel1.scanline}/{videocore.HvsChannel1.height} overhead: {overhead} next: {next_delay}") + end = time.monotonic() + overhead = end - start + time.sleep(max(0, next_delay - overhead)) + + start = time.monotonic() next_delay = gif.next_frame() if (x > 0) and (x < xmax): x += xinc @@ -45,10 +55,7 @@ def gif_animate(): y = 0 sprite.x = x sprite.y = y - start = time.monotonic() videocore.HvsChannel1.set_sprite_list([sprite]) - end = time.monotonic() - refresh = end-start #print("refresh time:") #print(refresh) @@ -80,7 +87,29 @@ def many_formats(): format4 = make_color_sweep(4, dist * 3, color_order=3, red_start=0, red_max=31, green_start=5, green_max=63, blue_start = 11, blue_max=31) videocore.HvsChannel1.set_sprite_list([format1, format2, format3, format4]) -#many_formats() -gif_animate() +def tilegrid(): + import adafruit_imageload + import displayio + import board + # https://learn.adafruit.com/circuitpython-display-support-using-displayio/sprite-sheet + sprite_sheet, palette = adafruit_imageload.load("/cp_sprite_sheet.bmp", bitmap=displayio.Bitmap, palette=displayio.Palette) + sprite = displayio.TileGrid(sprite_sheet, pixel_shader=palette, width=2, height=2, tile_width=16, tile_height=16) + group = displayio.Group(scale=8) + group.append(sprite) + display = board.DISPLAY + group.x = 30 + group.y = 30 + for i in range(30): + sprite[0] = i % 6 + sprite[1] = (i+1) % 6 + sprite[2] = (i+2) % 6 + sprite[3] = (i+3) % 6 + for j in range(100): + group.x = j + display.show(group) + return + time.sleep(0.01) -print("Hello World!") +#many_formats() +#gif_animate() +tilegrid() diff --git a/ports/broadcom/boards/raspberrypi_zero/board.c b/ports/broadcom/boards/raspberrypi_zero/board.c index ab9454298c..b8141e2a3b 100644 --- a/ports/broadcom/boards/raspberrypi_zero/board.c +++ b/ports/broadcom/boards/raspberrypi_zero/board.c @@ -30,8 +30,14 @@ #include "bindings/videocore/Framebuffer.h" #include "shared-module/displayio/__init__.h" #include "shared-bindings/framebufferio/FramebufferDisplay.h" +#include "bindings/videocore/Hvs.h" void board_init(void) { + hvs_channel_t *display = &allocate_display()->hvs_display; + display->channel = 1; + display->base.type = &hvs_channel_type; + return; + /* videocore_framebuffer_obj_t *fb = &allocate_display_bus()->videocore; fb->base.type = &videocore_framebuffer_type; common_hal_videocore_framebuffer_construct(fb, 640, 480); @@ -43,6 +49,7 @@ void board_init(void) { MP_OBJ_FROM_PTR(fb), 0, true); + */ } // Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/broadcom/common-hal/microcontroller/__init__.c b/ports/broadcom/common-hal/microcontroller/__init__.c index 38fa5c7072..082d5d28e5 100644 --- a/ports/broadcom/common-hal/microcontroller/__init__.c +++ b/ports/broadcom/common-hal/microcontroller/__init__.c @@ -60,7 +60,7 @@ void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) { // there is room for a 6bit int to be smuggled across reset, look into using that } - +// https://learn.adafruit.com/circuitpython-essentials/circuitpython-resetting void common_hal_mcu_reset(void) { common_hal_mcu_disable_interrupts(); *REG32(PM_WDOG) = PM_PASSWORD | (1 & PM_WDOG_MASK); diff --git a/shared-module/displayio/TileGrid.c b/shared-module/displayio/TileGrid.c index d1e2389c23..dac5d060b0 100644 --- a/shared-module/displayio/TileGrid.c +++ b/shared-module/displayio/TileGrid.c @@ -38,7 +38,7 @@ void common_hal_displayio_tilegrid_construct(displayio_tilegrid_t *self, mp_obj_ mp_obj_t pixel_shader, uint16_t width, uint16_t height, uint16_t tile_width, uint16_t tile_height, uint16_t x, uint16_t y, uint8_t default_tile) { uint32_t total_tiles = width * height; - // Sprites will only have one tile so save a little memory by inlining values in the pointer. + // Sprites with only have one tile so save a little memory by inlining values in the pointer. uint8_t inline_tiles = sizeof(uint8_t *); if (total_tiles <= inline_tiles) { self->tiles = 0; diff --git a/shared-module/displayio/TileGrid.h b/shared-module/displayio/TileGrid.h index 4f5cfbc62f..4b789b51d7 100644 --- a/shared-module/displayio/TileGrid.h +++ b/shared-module/displayio/TileGrid.h @@ -40,7 +40,7 @@ typedef struct { mp_obj_t pixel_shader; int16_t x; int16_t y; - uint16_t pixel_width; + uint16_t pixel_width; // of the entire grid, covering width_in_tiles tiles uint16_t pixel_height; uint16_t bitmap_width_in_tiles; ; diff --git a/shared-module/displayio/__init__.h b/shared-module/displayio/__init__.h index 1add0133b6..0388148270 100644 --- a/shared-module/displayio/__init__.h +++ b/shared-module/displayio/__init__.h @@ -50,6 +50,7 @@ // Port unique frame buffers. #if CIRCUITPY_VIDEOCORE #include "bindings/videocore/Framebuffer.h" +#include "bindings/videocore/Hvs.h" #endif #if CIRCUITPY_PICODVI #include "bindings/picodvi/Framebuffer.h" @@ -88,6 +89,7 @@ typedef struct { displayio_epaperdisplay_obj_t epaper_display; #if CIRCUITPY_FRAMEBUFFERIO framebufferio_framebufferdisplay_obj_t framebuffer_display; + hvs_channel_t hvs_display; #endif }; } primary_display_t;