aboutsummaryrefslogtreecommitdiff
path: root/kernel/drivers/bga.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drivers/bga.c')
-rw-r--r--kernel/drivers/bga.c127
1 files changed, 127 insertions, 0 deletions
diff --git a/kernel/drivers/bga.c b/kernel/drivers/bga.c
new file mode 100644
index 0000000..2f3b1fa
--- /dev/null
+++ b/kernel/drivers/bga.c
@@ -0,0 +1,127 @@
+// MIT License, Copyright (c) 2021 Marvin Borner
+
+#include <assert.h>
+#include <def.h>
+#include <drivers/bga.h>
+#include <drivers/cpu.h>
+#include <drivers/pci.h>
+#include <fb.h>
+#include <io.h>
+#include <mem.h>
+#include <mm.h>
+
+#define BGA_ADDRESS 0x01ce
+#define BGA_DATA 0x01cf
+
+#define BGA_VERSION 0
+#define BGA_XRES 1
+#define BGA_YRES 2
+#define BGA_BPP 3
+#define BGA_ENABLE 4
+#define BGA_BANK 5
+#define BGA_VWIDTH 6
+#define BGA_VHEIGHT 7
+#define BGA_XOFF 8
+#define BGA_YOFF 9
+
+#define BGA_LINEAR_FB 0x40
+
+#define BGA_V0 0xb0c0
+#define BGA_V1 0xb0c1
+#define BGA_V2 0xb0c2
+#define BGA_V3 0xb0c3
+#define BGA_V4 0xb0c4
+#define BGA_V5 0xb0c5
+
+PROTECTED static u32 bga_device_pci = 0;
+PROTECTED static struct fb_generic generic = { 0 };
+
+CLEAR static void bga_find(u32 device, u16 vendor_id, u16 device_id, void *extra)
+{
+ if ((vendor_id == 0x1234) && (device_id == 0x1111))
+ *((u32 *)extra) = device;
+}
+
+static void bga_write_reg(u16 address, u16 data)
+{
+ outw(BGA_ADDRESS, address);
+ outw(BGA_DATA, data);
+}
+
+static u16 bga_read_reg(u16 index)
+{
+ outw(BGA_ADDRESS, index);
+ return inw(BGA_DATA);
+}
+
+CLEAR u8 bga_available(void)
+{
+ pci_scan(&bga_find, -1, &bga_device_pci);
+ u16 status = bga_read_reg(BGA_VERSION);
+ return bga_device_pci != 0 && status >= BGA_V0 && status <= BGA_V5;
+}
+
+CLEAR static u32 bga_fb_base(void)
+{
+ u32 bar0 = pci_read_field(bga_device_pci, PCI_BAR0, 4);
+ assert(!(bar0 & 7)); // MMIO32
+ return bar0 & 0xfffffff0;
+}
+
+// TODO: BGA resolution using control calls
+static u32 fb_owner = 0;
+static res bga_control(u32 request, void *arg1, void *arg2, void *arg3)
+{
+ UNUSED(arg3);
+
+ switch (request) {
+ case IOCTL_FB_GET: {
+ if (!generic.fb)
+ return -ENOENT;
+
+ u32 size = MIN(sizeof(generic), (u32)arg2);
+ if (!memory_writable_range(memory_range(arg1, size)))
+ return -EFAULT;
+
+ if (fb_owner != 0 && proc_from_pid(fb_owner))
+ return -EBUSY;
+ fb_owner = proc_current()->pid;
+
+ u32 fb = fb_map_buffer(proc_current()->page_dir, &generic);
+
+ stac();
+ memcpy(arg1, &generic, size);
+ ((struct fb_generic *)arg1)->fb = (u8 *)fb;
+ clac();
+
+ return EOK;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+CLEAR static void bga_enable(u16 width, u16 height, u16 depth)
+{
+ bga_write_reg(BGA_ENABLE, 0);
+ bga_write_reg(BGA_XRES, width);
+ bga_write_reg(BGA_YRES, height);
+ bga_write_reg(BGA_BPP, depth);
+ bga_write_reg(BGA_ENABLE, 1 | BGA_LINEAR_FB);
+
+ generic.pitch = width * (depth >> 3);
+ generic.bpp = depth;
+ generic.width = width;
+ generic.height = height;
+ generic.fb = (u8 *)bga_fb_base();
+ fb_protect(&generic);
+}
+
+CLEAR void bga_install(void)
+{
+ bga_enable(1920, 1200, 32);
+
+ struct io_dev *dev = zalloc(sizeof(*dev));
+ dev->control = bga_control;
+ io_add(IO_FRAMEBUFFER, dev);
+}