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 8537de9
Show file tree
Hide file tree
Showing 7 changed files with 375 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
95 changes: 95 additions & 0 deletions ports/broadcom/bindings/videocore/hvs.h
@@ -0,0 +1,95 @@
#pragma once

enum alpha_mode {
alpha_mode_pipeline = 0, // per-pixel alpha allowed, POS0_ALPHA ignored
alpha_mode_fixed = 1, // use POS0_ALPHA() for entire sprite
alpha_mode_fixed_nonzero = 2, // POS0_ALPHA() and per-pixel both have an effect
alpha_mode_fixed_over_7 = 3,
};

enum hvs_pixel_format {
/* 8bpp */
HVS_PIXEL_FORMAT_RGB332 = 0,
/* 16bpp */
HVS_PIXEL_FORMAT_RGBA4444 = 1,
HVS_PIXEL_FORMAT_RGB555 = 2,
HVS_PIXEL_FORMAT_RGBA5551 = 3,
HVS_PIXEL_FORMAT_RGB565 = 4,
/* 24bpp */
HVS_PIXEL_FORMAT_RGB888 = 5,
HVS_PIXEL_FORMAT_RGBA6666 = 6,
/* 32bpp */
HVS_PIXEL_FORMAT_RGBA8888 = 7,

HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE = 8,
HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE = 9,
HVS_PIXEL_FORMAT_YCBCR_YUV422_3PLANE = 10,
HVS_PIXEL_FORMAT_YCBCR_YUV422_2PLANE = 11,
HVS_PIXEL_FORMAT_H264 = 12,
HVS_PIXEL_FORMAT_PALETTE = 13,
HVS_PIXEL_FORMAT_YUV444_RGB = 14,
HVS_PIXEL_FORMAT_AYUV444_RGB = 15,
HVS_PIXEL_FORMAT_RGBA1010102 = 16,
HVS_PIXEL_FORMAT_YCBCR_10BIT = 17,
};

struct hvs_channel {
volatile uint32_t dispctrl;
volatile uint32_t dispbkgnd;
volatile uint32_t dispstat;
// 31:30 mode
// 29 full
// 28 empty
// 17:12 frame count
// 11:0 line
volatile uint32_t dispbase;
};

extern volatile struct hvs_channel *hvs_hw_channels;

#define CONTROL_FORMAT(n) (n & 0xf)
#define CONTROL_END (1<<31)
#define CONTROL_VALID (1<<30)
#define CONTROL_WORDS(n) (((n) & 0x3f) << 24)
#define CONTROL0_FIXED_ALPHA (1<<19)
#define CONTROL0_HFLIP (1<<16)
#define CONTROL0_VFLIP (1<<15)
#define CONTROL_PIXEL_ORDER(n) ((n & 3) << 13)
#define CONTROL_SCL1(scl) (scl << 8)
#define CONTROL_SCL0(scl) (scl << 5)
#define CONTROL_UNITY (1<<4)

#define POS0_X(n) (n & 0xfff)
#define POS0_Y(n) ((n & 0xfff) << 12)
#define POS0_ALPHA(n) ((n & 0xff) << 24)

#define POS2_W(n) (n & 0xffff)
#define POS2_H(n) ((n & 0xffff) << 16)

#define HVS_PIXEL_ORDER_RGBA 0
#define HVS_PIXEL_ORDER_BGRA 1
#define HVS_PIXEL_ORDER_ARGB 2
#define HVS_PIXEL_ORDER_ABGR 3

#define HVS_PIXEL_ORDER_XBRG 0
#define HVS_PIXEL_ORDER_XRBG 1
#define HVS_PIXEL_ORDER_XRGB 2
#define HVS_PIXEL_ORDER_XBGR 3

#if BCM_VERSION == 2835
#define BCM_PERIPH_BASE_VIRT 0x20000000
#endif
#define SCALER_BASE (BCM_PERIPH_BASE_VIRT + 0x400000)

#define SCALER_DISPCTRL0 (SCALER_BASE + 0x40)
#define SCALER_DISPCTRLX_ENABLE (1<<31)
#define SCALER_DISPCTRLX_RESET (1<<30)
#define SCALER_DISPCTRL_W(n) ((n & 0xfff) << 12)
#define SCALER_DISPCTRL_H(n) (n & 0xfff)

#define SCALER_DISPLIST0 (SCALER_BASE + 0x20)
#define SCALER_DISPLIST1 (SCALER_BASE + 0x24)
#define SCALER_DISPLIST2 (SCALER_BASE + 0x28)

#define SCALER_LIST_MEMORY (SCALER_BASE + 0x2000)
#define SCALER5_LIST_MEMORY (SCALER_BASE + 0x4000)

0 comments on commit 8537de9

Please sign in to comment.