Skip to content

Commit

Permalink
initial broadcom 2d accel code
Browse files Browse the repository at this point in the history
  • Loading branch information
cleverca22 committed Aug 30, 2023
1 parent 337b800 commit eff21fc
Show file tree
Hide file tree
Showing 6 changed files with 280 additions and 0 deletions.
2 changes: 2 additions & 0 deletions ports/broadcom/Makefile
Expand Up @@ -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 \
Expand Down
125 changes: 125 additions & 0 deletions ports/broadcom/bindings/videocore/Hvs.c
@@ -0,0 +1,125 @@
#include <stdlib.h>
#include <stdio.h>

#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; i<len; i++) {
mp_obj_t sprite = mp_arg_validate_type(items[i], &hvs_sprite_type, MP_QSTR_todo);
sprite_t *s = MP_OBJ_TO_PTR(sprite);
c_maybe_regen(sprite);
uint8_t length = (s->dlist[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; i<len; i++) {
mp_obj_t sprite = mp_arg_validate_type(items[i], &hvs_sprite_type, MP_QSTR_todo);
sprite_t *s = MP_OBJ_TO_PTR(sprite);
uint8_t length = (s->dlist[0] >> 24) & 0x3f;
for (int j=0; j<length; j++) {
dlist_memory[dlist_slot++] = s->dlist[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,
};

8 changes: 8 additions & 0 deletions 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];
120 changes: 120 additions & 0 deletions ports/broadcom/bindings/videocore/Sprite.c
@@ -0,0 +1,120 @@
#include <stdlib.h>
#include <stdio.h>

#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(
//),
};
19 changes: 19 additions & 0 deletions 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);
6 changes: 6 additions & 0 deletions ports/broadcom/bindings/videocore/__init__.c
Expand Up @@ -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);
Expand Down

0 comments on commit eff21fc

Please sign in to comment.