Orion
Barry Importing existing Orion kernel d41a53c (2 years, 4 months ago)diff --git a/drivers/vga/bga.c b/drivers/vga/bga.c new file mode 100644 index 0000000..aff91aa --- /dev/null +++ b/drivers/vga/bga.c @@ -0,0 +1,131 @@ +/* + * This file implements the driver for the Bochs Graphics Adapter video card. + * It is a simple card available in virtual machines. It creates a framebuffer + * device node, and handles the reading/writing to it, and also mapping the + * framebuffer into memory. + */ + +#include <stdint.h> +#include <string.h> +#include <sys/fb.h> +#include "../drivers.h" +#include "../pci.h" +#include "../../mem/paging.h" +#include "../../vfs/vfs.h" +#include "../../io.h" + +size_t bga_write(File *file, char *buf, size_t size, off_t offset); +int bga_ioctl(File *file, unsigned long request, uintptr_t argp); + +FileOps bgaFileOps = { + .write = bga_write, + .ioctl = bga_ioctl, +}; + +Driver bgaDriver = { + .major = 3, + .ops = &bgaFileOps, + .next = NULL, +}; + +static uint32_t bgaWidth, bgaHeight, bgaBpp; +static uintptr_t lfb; + +enum BGAIndex { + BGA_INDEX_ID, + BGA_INDEX_XRES, + BGA_INDEX_YRES, + BGA_INDEX_BPP, + BGA_INDEX_ENABLE, + BGA_INDEX_BANK, + BGA_INDEX_VIRT_WIDTH, + BGA_INDEX_VIRT_HEIGHT, + BGA_INDEX_XOFF, + BGA_INDEX_YOFF, +}; + +/* Write a BGA register */ +void +bga_write_register(uint16_t index, uint16_t data) +{ + outw(0x1CE, index); + outw(0x1CF, data); +} + +/* Set the video mode */ +void +bga_set_mode(uint32_t width, uint32_t height, uint32_t bpp) +{ + bgaWidth = width; + bgaHeight = height; + bgaBpp = bpp; + bga_write_register(BGA_INDEX_ENABLE, 0); + bga_write_register(BGA_INDEX_XRES, width); + bga_write_register(BGA_INDEX_YRES, height); + bga_write_register(BGA_INDEX_BPP, bpp); + bga_write_register(BGA_INDEX_ENABLE, 1 | 0x40); + /* 0x40 for framebuffer */ +} + +/* Initialise the BGA card */ +void +bga_init(uint8_t bus, uint8_t slot, uint8_t func) +{ + register_driver(&bgaDriver); + + const int width = 1280; + const int height = 720; + const int bpp = 32; + bga_set_mode(width, height, bpp); + lfb = pci_read_dword(bus, slot, func, 0x10) & 0xFFFFFFF0; + for (uintptr_t i = 0; i < width*height*(bpp/8); i += 0x1000) + *get_page((void *) lfb + i) = (0xFD000000 + i) | PTE_PRESENT + | PTE_WRITE | PTE_GLOBAL; + /* use the MMAP method to map pages of it into a page dir */ + mknod("/dev/fb", S_IFCHR | 0600, MKDEV(bgaDriver.major, 0)); +} + +/* Write to the BGA framebuffer */ +size_t +bga_write(File *file, char *buf, size_t size, off_t offset) +{ + size_t max, count = 0; + page_t oldpg; + while (size) { + max = (size < 0x1000) ? size : 0x1000; + acquire(&quickPageLock); + oldpg = quick_page(lfb + PG_ADDR(offset) + count); + memcpy(QUICK_PAGE, buf + count, max); + quick_page(oldpg); + release(&quickPageLock); + count += max; + size -= max; + } + return count; +} + +/* Control the BGA framebuffer/device */ +int +bga_ioctl(File *file, unsigned long request, uintptr_t argp) +{ + FBVarInfo *vinfo; + FBFixInfo *finfo; + + switch (request) { + case FBIOGET_VSCREENINFO: + vinfo = (void *) argp; + vinfo->xres = bgaWidth; + vinfo->yres = bgaHeight; + vinfo->bpp = bgaBpp; + return 0; + case FBIOPUT_VSCREENINFO: + vinfo = (void *) argp; + bga_set_mode(vinfo->xres, vinfo->yres, vinfo->bpp); + return 0; + case FBIOGET_FSCREENINFO: + finfo = (void *) argp; + finfo->fbmem = 0xFD000000; // lfb; + finfo->fbmemLen = bgaWidth*bgaHeight*(bgaBpp/8); + return 0; + } +}