aboutsummaryrefslogtreecommitdiff
path: root/kernel/drivers/bga.c
blob: cc93b7519542da6cf1426d53d189fef2426b7589 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// MIT License, Copyright (c) 2021 Marvin Borner

#include <assert.h>
#include <def.h>
#include <dev.h>
#include <drivers/bga.h>
#include <drivers/cpu.h>
#include <drivers/pci.h>
#include <fb.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 DEVCTL_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 dev_dev *dev = zalloc(sizeof(*dev));
	dev->control = bga_control;
	dev_add(DEV_FRAMEBUFFER, dev);
}