title: Raspberry 5 baremetal framebuffer example keywords:raspi5,asm,c,kernel,arm64 # Raspberry 5 baremetal framebuffer example ## Intro The Raspberry Pi 5 has an easy way to draw graphics on the display by using a communication protocol through mailboxes. Here we will get framebuffer address and draw some simple primitives onto screen. There is not much clear documentation on how things should work all based on current examples and experimenting. ## Programming mailbox ### Packets To communicate with the mailbox, you need to create an array with commands, input and output elements. Array elements are uint32_t. ```c array[0] //first element ... array[n] //last element ``` First element is size of array in bytes and request tag, last value is the last tag value Here is the list of mailbox commands https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface commands consist of tag, amount of input bytes and output bytes. Example of allocate buffer command: ```txt Tag: 0x00040001 Request: Length: 4 Value: u32: alignment in bytes Response: Length: 8 Value: u32: frame buffer base address in bytes u32: frame buffer size in bytes ``` this translates to array values ```c cmd[n+0] = 0x00040001 cmd[n+1] = 8 cmd[n+2] = 4 cmd[n+3] = 0 cmd[n+4] = 0 ``` ### Addresses ## Setting up the framebuffer Raspberry Pi5 doesn't provide much that you can do with framebuffers comparing to version 4. And there isn't much documentation on full capabilities. Simple example how to use framebuffer is request for framebuffer address and directly write values to the memmory range. ```c fb_mbox[0] = 8*4; fb_mbox[1] = MBOX_REQUEST; fb_mbox[2] = MBOX_TAG_GETFB; fb_mbox[3] = 8; //buffer size fb_mbox[4] = 4; //alignment fb_mbox[5] = 0; //frame buffer address fb_mbox[6] = 0; //size in bytes fb_mbox[7] = MBOX_TAG_LAST; ``` The result will be in fb_mbox[5], which needs to be masked and can then be accessed directly: ```c fb_address = fb_mbox[5] & 0x3fffffff; ``` buffer must be aligned by 16-bytes. To submit the buffer to mailbox for processing write to MBOX1_WRITE register. Check for response by reading MBOX1_STATUS register. Check MBOX0_READ that its matches the request address. And check the response status status it will be located at second index of request buffer. ## Drawing pixel Drawing a pixel is as simple as calculating pixel position and write 32bit value ```c void fb_draw_pixel(unsigned int x, unsigned int y) { mmio_write(fb_address + (((SCREEN_X*y)+x)*4),0xFFFFFFFF); } ``` extended example is with using colors in format ARGB32 ```c void fb_draw_pixel_rgb(unsigned int x, unsigned int y, unsigned char r, unsigned char g, unsigned char b) { mmio_write(fb_address + (((SCREEN_X*y)+x)*4),0xFF000000|((unsigned int)r<<16)|((unsigned int)g<<8)|((unsigned int)b)); } ``` ## Source code __config.txt__ ```txt # Core settings arm_64bit=1 kernel=kernel8.img [all] # UART configuration for debugging enable_uart=1 uart_2ndstage=1 uart_debug=1 uart_clkrate=48000000 uart_baudrate=115200 # RP1 chip configuration (Pi5 I/O chip) # Disable PCIe reset to access RP1 from bare metal pciex4_reset=0 # Memory settings gpu_mem=64 total_mem=1024 # Boot settings disable_splash=1 boot_delay=0 # Debug settings uart_2ndstage=1 # Pi5 specific GPIO/UART dtparam=uart0=on # Disable unused services to reduce boot time enable_jtag_gpio=1 enable_rp1_uart=1 pciex4_reset=0 core_freq_min=500 hdmi_group=1 hdmi_mode=16 ``` __mailbox.h__ ```c #ifndef __MAILBOX_H #define __MAILBOX_H //#define MMIO_BASE 0x1c00000000UL #define MMIO_BASE_1 0x107C000000 //#define MMIO_BASE 0xc00000000UL //#define MBOX_REG_OFFSET (MMIO_BASE+0x40008000) //#define MBOX_REG_OFFSET (MMIO_BASE+0x8000) #define MBOX_REG_OFFSET (MMIO_BASE_1+0x13880) //#define MBOX_REG_OFFSET (MMIO_BASE+0xB880) #define MBOX_COMMAND_BUFFER_SIZE 36 #define MBOX1_OFFSSET 0x20 #define MBOX0_READ (MBOX_REG_OFFSET + 0x0) #define MBOX0_POLL (MBOX_REG_OFFSET + 0x10) #define MBOX0_SENDER (MBOX_REG_OFFSET + 0x14) #define MBOX0_STATUS (MBOX_REG_OFFSET + 0x18) #define MBOX0_CONFIG (MBOX_REG_OFFSET + 0x1C) #define MBOX1_WRITE (MBOX_REG_OFFSET+MBOX1_OFFSSET + 0x00) #define MBOX1_STATUS (MBOX_REG_OFFSET+MBOX1_OFFSSET + 0x18) #define MBOX_RESPONSE 0x80000000 #define MBOX_RESP_ERR 0x80000001 #define MBOX_FULL 0x80000000 #define MBOX_EMPTY 0x40000000 #define MBOX_REQUEST 0x00000000 #define MBOX_CH_VIDEOCORE 0x8 #define MBOX_TAG_FW_VERSION 0x00001 #define MBOX_TAG_SETPOWER 0x28001 #define MBOX_TAG_SETCLKRATE 0x38002 #define MBOX_TAG_SETPHYWH 0x48003 #define MBOX_TAG_SETVIRTWH 0x48004 #define MBOX_TAG_SETVIRTOFF 0x48009 #define MBOX_TAG_SETDEPTH 0x48005 #define MBOX_TAG_SETPXLORDR 0x48006 #define MBOX_TAG_GETFB 0x40001 #define MBOX_TAG_GETPITCH 0x40008 #define MBOX_TAG_LAST 0x00000 //unsigned int mbox_call(volatile unsigned int *request, unsigned char channel); unsigned int mbox_call(unsigned char channel); #endif ``` __mailbox.c__ ```c #include #include #include extern volatile unsigned int __attribute__((aligned(16))) fb_mbox[MBOX_COMMAND_BUFFER_SIZE]; extern unsigned int fb_address; //unsigned int mbox_call(volatile unsigned int *request, unsigned char channel) { unsigned int mbox_call(unsigned char channel) { unsigned int addr = ((unsigned int)((unsigned long) &fb_mbox) & ~0xF) | (channel & 0xF); while ((mmio_read(MBOX1_STATUS) & MBOX_FULL) != 0); unsigned int p_addr = (uint32_t)fb_mbox; mmio_write(MBOX1_WRITE,MBOX_CH_VIDEOCORE|p_addr); while (1) { while (mmio_read(MBOX1_STATUS) & MBOX_EMPTY); if (addr == mmio_read(MBOX0_READ)) { return fb_mbox[1] == MBOX_RESPONSE; } } } ``` __framebuffer.h__ ```c #ifndef __FRAMEBUFFER_H #define __FRAMEBUFFER_H void fb_init(); void fb_fw_version(); void fb_draw_pixel(unsigned int x, unsigned int y); #endif ``` __framebuffer.c__ ```c #include #include #include #include #define FB_SCREEN_WIDTH 1920 #define FB_SCREEN_HEIGHT 1080 volatile unsigned int __attribute__((aligned(16))) fb_mbox[MBOX_COMMAND_BUFFER_SIZE]; unsigned int fb_address = 0x0; unsigned int width, height, pitch, isrgb; unsigned char *fb; void fb_init() { fb_mbox[0] = 8*4; fb_mbox[1] = MBOX_REQUEST; fb_mbox[2] = MBOX_TAG_GETFB; fb_mbox[3] = 8; fb_mbox[4] = 4; fb_mbox[5] = 0; //frame buffer address fb_mbox[6] = 0; fb_mbox[7] = MBOX_TAG_LAST; mbox_call(MBOX_CH_VIDEOCORE); pl011_puts("mbox_call read\r\n"); pl011_puts("fw version "); pl011_putu32(fb_mbox[5]); fb_address = fb_mbox[5] & 0x3fffffff; pl011_puts("\r\n"); } void fb_fw_version() { fb_mbox[0] = 5*4; fb_mbox[1] = MBOX_REQUEST; fb_mbox[2] = MBOX_TAG_FW_VERSION; fb_mbox[3] = 4; fb_mbox[4] = 0; //fw version fb_mbox[5] = MBOX_TAG_LAST; mbox_call(MBOX_CH_VIDEOCORE); //pl011_puts("mbox_call read\r\n"); pl011_puts("fw version "); pl011_putu32(fb_mbox[4]); pl011_puts("\r\n"); } #define SCREEN_X 1920 #define SCREEN_Y 1080 #define BITS_PER_PIXEL 32 void fb_draw_pixel(unsigned int x, unsigned int y) { mmio_write(fb_address + (((SCREEN_X*y)+x)*4),0xFFFFFFFF); } ``` ## Links [/writeup/raspberry5_baremetal_helloworld.md](/writeup/raspberry5_baremetal_helloworld.md) https://www.rpi4os.com/part5-framebuffer/ https://github.com/babbleberry/rpi4-osdev/blob/master/part5-framebuffer/mb.h https://github.com/valvers/arm-tutorial-rpi/blob/master/part-5/armc-017/rpi-mailbox-interface.h https://www.valvers.com/open-software/raspberry-pi/bare-metal-programming-in-c-part-5/#mailboxes https://github.com/raspberrypi/linux/blob/rpi-6.12.y/arch/arm64/boot/dts/broadcom/bcm2712.dtsi#L229C3-L234C5 https://blog.benjdoherty.com/2019/05/21/Exploring-Hardware-Compositing-With-the-Raspberry-Pi/#Double-Buffering-the-Display-List https://github.com/bejado/raspberry-pi-os/blob/master/src/hvs/src/kernel.c https://github.com/librerpi/lk-overlay/blob/master/platform/bcm28xx/mailbox/mailbox.c https://github.com/raspberrypi/linux/pull/6312/files https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface https://github.com/raspberrypi/linux/blob/rpi-6.12.y/drivers/mailbox/bcm2835-mailbox.c https://github.com/rsta2/circle/blob/master/include/circle/bcm2835.h https://github.com/raspberrypi/linux/blob/rpi-6.12.y/include/soc/bcm2835/raspberrypi-firmware.h https://bitbanged.com/posts/understanding-rpi/the-mailbox/ https://github.com/BrianSidebotham/arm-tutorial-rpi/blob/master/part-5/readme.md https://fossies.org/linux/qemu/hw/misc/bcm2835_mbox.c https://satyria.de/arm/index.php?title=Graphics_in_C_(PI5) https://forums.raspberrypi.com/viewtopic.php?t=380434 https://grasslab.github.io/osdi/en/hardware/mailbox.html