diff --git a/ports/broadcom/Makefile b/ports/broadcom/Makefile index 7243091a42913..6d850d042c973 100644 --- a/ports/broadcom/Makefile +++ b/ports/broadcom/Makefile @@ -54,6 +54,8 @@ INC += -I. \ SRC_C += bindings/videocore/__init__.c \ bindings/videocore/Framebuffer.c \ + bindings/videocore/Sprite.c \ + bindings/videocore/Hvs.c \ boards/$(BOARD)/board.c \ boards/$(BOARD)/pins.c \ background.c \ diff --git a/ports/broadcom/bindings/videocore/Hvs.c b/ports/broadcom/bindings/videocore/Hvs.c new file mode 100644 index 0000000000000..645dce43c1d07 --- /dev/null +++ b/ports/broadcom/bindings/videocore/Hvs.c @@ -0,0 +1,125 @@ +#include +#include + +#include "py/obj.h" +#include "py/objproperty.h" + +#include "bindings/videocore/Sprite.h" +#include "shared-bindings/displayio/Bitmap.h" +#include "py/runtime.h" + +#include "bindings/videocore/hvs.h" +#include "bindings/videocore/Hvs.h" + +#if BCM_VERSION == 2711 +volatile uint32_t* dlist_memory = (volatile uint32_t*)SCALER5_LIST_MEMORY; +#else +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 + +hvs_channel_t hvs_channels[3] = { + [0] = { + .base.type = &hvs_channel_type, + .channel = 0, + }, + [1] = { + .base.type = &hvs_channel_type, + .channel = 1, + }, + [2] = { + .base.type = &hvs_channel_type, + .channel = 2, + }, +}; + +static mp_obj_t c_set_sprite_list(mp_obj_t self_in, mp_obj_t list) { + 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); + uint32_t needed_slots = 1; // one more, to terminate the list + for (uint32_t i=0; idlist[0] >> 24) & 0x3f; + needed_slots += length; + } + if (needed_slots > (4096/2)) { + mp_raise_ValueError(translate("too many sprites, unable to pageflip reliably")); + } + if ((dlist_slot + needed_slots) > 4096) { + // early loop) + dlist_slot = 128; + } + uint32_t starting_slot = dlist_slot; + for (uint32_t i=0; idlist[0] >> 24) & 0x3f; + for (int j=0; jdlist[j]; + } + } + dlist_memory[dlist_slot++] = CONTROL_END; + switch (self->channel) { + case 0: + *((volatile uint32_t*)SCALER_DISPLIST0) = starting_slot; + break; + case 1: + *((volatile uint32_t*)SCALER_DISPLIST1) = starting_slot; + break; + case 2: + *((volatile uint32_t*)SCALER_DISPLIST2) = starting_slot; + break; + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(fun_set_sprite_list, c_set_sprite_list); + +#define simpleprop(name) \ + static MP_DEFINE_CONST_FUN_OBJ_1(fun_get_##name, c_getter_##name); \ + static MP_PROPERTY_GETTER(prop_##name, (mp_obj_t)&fun_get_##name) + +#define prop_entry(name) { MP_ROM_QSTR(MP_QSTR_##name), MP_ROM_PTR(&prop_##name) } + +static mp_obj_t c_getter_width(mp_obj_t self_in) { + hvs_channel_t *self = MP_OBJ_TO_PTR(self_in); + uint32_t ctrl = hvs_hw_channels[self->channel].dispctrl; + return MP_OBJ_NEW_SMALL_INT((ctrl >> 12) & 0xfff); +} +static mp_obj_t c_getter_height(mp_obj_t self_in) { + hvs_channel_t *self = MP_OBJ_TO_PTR(self_in); + uint32_t ctrl = hvs_hw_channels[self->channel].dispctrl; + return MP_OBJ_NEW_SMALL_INT(ctrl & 0xfff); +} +static mp_obj_t c_getter_enabled(mp_obj_t self_in) { + hvs_channel_t *self = MP_OBJ_TO_PTR(self_in); + uint32_t ctrl = hvs_hw_channels[self->channel].dispctrl; + return mp_obj_new_bool(ctrl & SCALER_DISPCTRLX_ENABLE); +} + +simpleprop(width); +simpleprop(height); +simpleprop(enabled); + +static const mp_rom_map_elem_t hvs_channel_locals_dict_table[] = { + prop_entry(width), + prop_entry(height), + prop_entry(enabled), + { MP_ROM_QSTR(MP_QSTR_set_sprite_list), MP_ROM_PTR(&fun_set_sprite_list) }, +}; + +static MP_DEFINE_CONST_DICT(hvs_channel_locals_dict, hvs_channel_locals_dict_table); + +const mp_obj_type_t hvs_channel_type = { + { &mp_type_type }, + .name = MP_QSTR_HvsChannel, + .locals_dict = (mp_obj_dict_t*)&hvs_channel_locals_dict, +}; + diff --git a/ports/broadcom/bindings/videocore/Hvs.h b/ports/broadcom/bindings/videocore/Hvs.h new file mode 100644 index 0000000000000..6df601791d229 --- /dev/null +++ b/ports/broadcom/bindings/videocore/Hvs.h @@ -0,0 +1,8 @@ +#pragma once + +typedef struct { + mp_obj_base_t base; + uint32_t channel; +} hvs_channel_t; + +extern hvs_channel_t hvs_channels[3]; diff --git a/ports/broadcom/bindings/videocore/Sprite.c b/ports/broadcom/bindings/videocore/Sprite.c new file mode 100644 index 0000000000000..aa8d066e9a969 --- /dev/null +++ b/ports/broadcom/bindings/videocore/Sprite.c @@ -0,0 +1,120 @@ +#include +#include + +#include "py/obj.h" +#include "py/objproperty.h" + +#include "bindings/videocore/Sprite.h" +#include "py/runtime.h" + + +STATIC mp_obj_t hvs_sprite_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + sprite_t *sprite = m_new_obj(sprite_t); + sprite->base.type = &hvs_sprite_type; + sprite->bitmap = NULL; + sprite->dirty = true; + sprite->alpha_mode = alpha_mode_fixed; + printf("sprite is at %p\n", sprite); + return MP_OBJ_FROM_PTR(sprite); +} + +enum hvs_pixel_format bitmap_to_hvs(const displayio_bitmap_t *bitmap) { + switch (bitmap->bits_per_value) { + case 16: + return HVS_PIXEL_FORMAT_RGB555; + } + return HVS_PIXEL_FORMAT_RGB332; +} + +#if BCM_VERSION == 2711 +#error not implemented yet +#else +void hvs_regen_noscale_noviewport(sprite_t *s) { + uint32_t *d = s->dlist; + // CTL0 + d[0] = CONTROL_VALID + | CONTROL_PIXEL_ORDER(HVS_PIXEL_ORDER_ABGR) + | CONTROL_UNITY + | CONTROL_FORMAT(bitmap_to_hvs(s->bitmap)) + | CONTROL_WORDS(7); + // 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); + // POS3, context + d[3] = 0xDEADBEEF; + // PTR0 + d[4] = ((uint32_t)s->bitmap->data) // assumes identity map, should be physical addr + | 0xc0000000; // and tell HVS to do uncached reads + // context 0 + d[5] = 0xDEADBEEF; + // pitch 0 + d[6] = s->bitmap->stride * 4; + + //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 + +mp_obj_t c_maybe_regen(mp_obj_t self_in) { + sprite_t *self = MP_OBJ_TO_PTR(self_in); + if (self->dirty) { + //printf("regen time\n"); + hvs_regen_noscale_noviewport(self); + self->dirty = false; + } else puts("not dirty"); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(fun_maybe_regen, c_maybe_regen); + +STATIC mp_obj_t get_image_c(mp_obj_t self_in) { + sprite_t *self = MP_OBJ_TO_PTR(self_in); + if (self->bitmap) return MP_OBJ_FROM_PTR(self->bitmap); + else return mp_const_none; +} +STATIC mp_obj_t set_image_c(mp_obj_t self_in, mp_obj_t value) { + displayio_bitmap_t *bitmap = mp_arg_validate_type(value, &displayio_bitmap_type, MP_QSTR_bitmap); + sprite_t *self = MP_OBJ_TO_PTR(self_in); + self->bitmap = bitmap; + self->dirty = true; + if (self->width == 0) self->width = bitmap->width; + if (self->height == 0) self->height = bitmap->height; + return value; +} +MP_DEFINE_CONST_FUN_OBJ_1(get_image_fun, get_image_c); +MP_DEFINE_CONST_FUN_OBJ_2(set_image_fun, set_image_c); +MP_PROPERTY_GETSET(image_prop, (mp_obj_t)&get_image_fun, (mp_obj_t)&set_image_fun); + +#define simpleprop(name) \ + static mp_obj_t c_getter_##name(mp_obj_t self_in) { sprite_t *self = MP_OBJ_TO_PTR(self_in); return MP_OBJ_NEW_SMALL_INT(self->name); } \ + static mp_obj_t c_setter_##name(mp_obj_t self_in, mp_obj_t value) { sprite_t *self = MP_OBJ_TO_PTR(self_in); self->name = mp_obj_get_int(value); self->dirty = true; return mp_const_none; } \ + static MP_DEFINE_CONST_FUN_OBJ_1(fun_get_##name, c_getter_##name); \ + static MP_DEFINE_CONST_FUN_OBJ_2(fun_set_##name, c_setter_##name); \ + static MP_PROPERTY_GETSET(prop_##name, (mp_obj_t)&fun_get_##name, (mp_obj_t)&fun_set_##name) + +#define prop_entry(name) { MP_ROM_QSTR(MP_QSTR_##name), MP_ROM_PTR(&prop_##name) } + +simpleprop(width); +simpleprop(height); +simpleprop(x); +simpleprop(y); + +STATIC const mp_rom_map_elem_t hvs_sprite_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_image), MP_ROM_PTR(&image_prop) }, + prop_entry(width), + prop_entry(height), + prop_entry(x), + prop_entry(y), + { MP_ROM_QSTR(MP_QSTR_maybe_regen), MP_ROM_PTR(&fun_maybe_regen) }, +}; + +STATIC MP_DEFINE_CONST_DICT(hvs_sprite_locals_dict, hvs_sprite_locals_dict_table); + +const mp_obj_type_t hvs_sprite_type = { + { &mp_type_type }, + .flags = 0, //MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_Sprite, + .locals_dict = (mp_obj_dict_t*)&hvs_sprite_locals_dict, + .make_new = hvs_sprite_make_new, + //MP_TYPE_EXTENDED_FIELDS( + //), +}; diff --git a/ports/broadcom/bindings/videocore/Sprite.h b/ports/broadcom/bindings/videocore/Sprite.h new file mode 100644 index 0000000000000..e6f21e7caa7b3 --- /dev/null +++ b/ports/broadcom/bindings/videocore/Sprite.h @@ -0,0 +1,19 @@ +#pragma once + +#include "shared-bindings/displayio/Bitmap.h" +#include "bindings/videocore/hvs.h" + +typedef struct { + mp_obj_base_t base; + displayio_bitmap_t *bitmap; + bool dirty; + uint32_t width; + uint32_t height; + uint32_t x; + uint32_t y; + uint32_t dlist[32]; + enum alpha_mode alpha_mode; +} sprite_t; + +extern const mp_obj_type_t hvs_sprite_type; +mp_obj_t c_maybe_regen(mp_obj_t self_in); diff --git a/ports/broadcom/bindings/videocore/__init__.c b/ports/broadcom/bindings/videocore/__init__.c index ffb90680ef12e..0070bdf91b1fe 100644 --- a/ports/broadcom/bindings/videocore/__init__.c +++ b/ports/broadcom/bindings/videocore/__init__.c @@ -30,12 +30,18 @@ #include "py/runtime.h" #include "bindings/videocore/Framebuffer.h" +#include "bindings/videocore/Sprite.h" +#include "bindings/videocore/Hvs.h" //| """Low-level routines for interacting with the Broadcom VideoCore GPU""" STATIC const mp_rom_map_elem_t videocore_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_videocore) }, { MP_ROM_QSTR(MP_QSTR_Framebuffer), MP_ROM_PTR(&videocore_framebuffer_type) }, + { MP_ROM_QSTR(MP_QSTR_Sprite), MP_ROM_PTR(&hvs_sprite_type) }, + { MP_ROM_QSTR(MP_QSTR_HvsChannel0), MP_OBJ_FROM_PTR(&hvs_channels[0]) }, + { MP_ROM_QSTR(MP_QSTR_HvsChannel1), MP_OBJ_FROM_PTR(&hvs_channels[1]) }, + { MP_ROM_QSTR(MP_QSTR_HvsChannel2), MP_OBJ_FROM_PTR(&hvs_channels[2]) }, }; STATIC MP_DEFINE_CONST_DICT(videocore_module_globals, videocore_module_globals_table);