diff options
| author | Arturs Artamonovs <dos21h@gmail.com> | 2025-12-03 22:57:46 +0000 |
|---|---|---|
| committer | Arturs Artamonovs <dos21h@gmail.com> | 2025-12-03 22:57:46 +0000 |
| commit | 04cce8c2178b20fe37554075f76fb6f141817b3f (patch) | |
| tree | 1e10791b53a005ef71f34d03585500b29433aaa3 | |
| parent | 600f4e5409e257d0e26fe2b22951ea78d8323af5 (diff) | |
| download | md-content-04cce8c2178b20fe37554075f76fb6f141817b3f.tar.gz md-content-04cce8c2178b20fe37554075f76fb6f141817b3f.zip | |
Add raspi5 framebuffers
| -rw-r--r-- | md/writeup/raspberry5_baremetal_framebuffer.md | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/md/writeup/raspberry5_baremetal_framebuffer.md b/md/writeup/raspberry5_baremetal_framebuffer.md new file mode 100644 index 0000000..681c486 --- /dev/null +++ b/md/writeup/raspberry5_baremetal_framebuffer.md @@ -0,0 +1,320 @@ +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 <mailbox.h> +#include <pl011.h> +#include <mmio.h> + +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 <framebuffer.h> +#include <mailbox.h> +#include <pl011.h> +#include <mmio.h> + +#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 + + |
