2021-06-04 03:20:23 +03:00
|
|
|
/**
|
2021-11-26 06:46:05 +03:00
|
|
|
* @file modules/i965.c
|
2021-06-04 03:20:23 +03:00
|
|
|
* @brief Bitbanged modeset driver for a ThinkPad T410's Intel graphics.
|
2022-02-06 16:08:20 +03:00
|
|
|
* @package x86_64
|
2021-06-04 03:20:23 +03:00
|
|
|
*
|
|
|
|
* This is NOT a viable driver for Intel graphics devices. It assumes Vesa
|
|
|
|
* has already properly set up the display pipeline with the needed timings
|
|
|
|
* for the panel on one particular model of Lenovo ThinkPad and then sets
|
|
|
|
* a handful of registers to get the framebuffer into the right resolution.
|
2021-11-26 06:46:05 +03:00
|
|
|
*
|
|
|
|
* @copyright
|
|
|
|
* This file is part of ToaruOS and is released under the terms
|
|
|
|
* of the NCSA / University of Illinois License - see LICENSE.md
|
|
|
|
* Copyright (C) 2021 K. Lange
|
2021-06-04 03:20:23 +03:00
|
|
|
*/
|
2021-07-17 12:55:54 +03:00
|
|
|
#include <errno.h>
|
2021-06-03 15:02:09 +03:00
|
|
|
#include <kernel/printf.h>
|
|
|
|
#include <kernel/types.h>
|
|
|
|
#include <kernel/video.h>
|
|
|
|
#include <kernel/pci.h>
|
|
|
|
#include <kernel/mmu.h>
|
|
|
|
#include <kernel/vfs.h>
|
|
|
|
#include <kernel/args.h>
|
2021-07-17 12:55:54 +03:00
|
|
|
#include <kernel/module.h>
|
2021-06-03 15:02:09 +03:00
|
|
|
|
2021-06-03 15:50:32 +03:00
|
|
|
#define REG_PIPEASRC 0x6001C
|
|
|
|
#define REG_PIPEACONF 0x70008
|
|
|
|
#define PIPEACONF_ENABLE (1 << 31)
|
|
|
|
#define PIPEACONF_STATE (1 << 30)
|
|
|
|
#define REG_DSPALINOFF 0x70184
|
|
|
|
#define REG_DSPASTRIDE 0x70188
|
|
|
|
#define REG_DSPASURF 0x7019c
|
|
|
|
|
2021-06-03 15:02:09 +03:00
|
|
|
extern fs_node_t * lfb_device;
|
2023-05-20 23:39:26 +03:00
|
|
|
extern int lfb_use_write_combining;
|
2021-06-03 15:02:09 +03:00
|
|
|
static uintptr_t ctrl_regs = 0;
|
|
|
|
|
|
|
|
static uint32_t i965_mmio_read(uint32_t reg) {
|
|
|
|
return *(volatile uint32_t*)(ctrl_regs + reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void i965_mmio_write(uint32_t reg, uint32_t val) {
|
|
|
|
*(volatile uint32_t*)(ctrl_regs + reg) = val;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void split(uint32_t val, uint32_t * a, uint32_t * b) {
|
|
|
|
*a = (val & 0xFFFF) + 1;
|
|
|
|
*b = (val >> 16) + 1;
|
|
|
|
}
|
|
|
|
|
2021-07-19 13:50:41 +03:00
|
|
|
static void i965_modeset(uint16_t x, uint16_t y) {
|
2021-06-03 15:02:09 +03:00
|
|
|
/* Disable pipe A while we update source size */
|
2021-06-03 15:50:32 +03:00
|
|
|
uint32_t pipe = i965_mmio_read(REG_PIPEACONF);
|
|
|
|
i965_mmio_write(REG_PIPEACONF, pipe & ~PIPEACONF_ENABLE);
|
|
|
|
while (i965_mmio_read(REG_PIPEACONF) & PIPEACONF_STATE);
|
2021-06-03 15:02:09 +03:00
|
|
|
|
|
|
|
/* Set source size */
|
2021-07-19 13:50:41 +03:00
|
|
|
i965_mmio_write(REG_PIPEASRC, ((x - 1) << 16) | (y - 1));
|
2021-06-03 15:02:09 +03:00
|
|
|
|
|
|
|
/* Re-enable pipe */
|
2021-06-03 15:50:32 +03:00
|
|
|
pipe = i965_mmio_read(REG_PIPEACONF);
|
|
|
|
i965_mmio_write(REG_PIPEACONF, pipe | PIPEACONF_ENABLE);
|
|
|
|
while (!(i965_mmio_read(REG_PIPEACONF) & PIPEACONF_STATE));
|
2021-06-03 15:02:09 +03:00
|
|
|
|
|
|
|
/* Keep the plane enabled while we update stride value */
|
2021-06-03 15:50:32 +03:00
|
|
|
i965_mmio_write(REG_DSPALINOFF, 0); /* offset to default of 0 */
|
2021-07-19 13:50:41 +03:00
|
|
|
i965_mmio_write(REG_DSPASTRIDE, x * 4); /* stride to 4 x width */
|
2021-06-03 15:50:32 +03:00
|
|
|
i965_mmio_write(REG_DSPASURF, 0); /* write to surface address triggers change; use default of 0 */
|
2021-06-03 15:02:09 +03:00
|
|
|
|
|
|
|
/* Update the values we expose to userspace. */
|
2021-07-19 13:50:41 +03:00
|
|
|
lfb_resolution_x = x;
|
|
|
|
lfb_resolution_y = y;
|
2021-06-03 15:02:09 +03:00
|
|
|
lfb_resolution_b = 32;
|
2021-06-03 15:50:32 +03:00
|
|
|
lfb_resolution_s = i965_mmio_read(REG_DSPASTRIDE);
|
2022-07-25 03:51:04 +03:00
|
|
|
lfb_memsize = lfb_resolution_s * lfb_resolution_y;
|
|
|
|
lfb_device->length = lfb_memsize;
|
2021-07-19 13:50:41 +03:00
|
|
|
}
|
|
|
|
|
2022-07-25 03:51:04 +03:00
|
|
|
extern void fbterm_draw_logo(void);
|
|
|
|
extern void fbterm_reset(void);
|
|
|
|
|
2021-07-19 13:50:41 +03:00
|
|
|
static void setup_framebuffer(uint32_t pcidev) {
|
|
|
|
/* Map BAR space for the control registers */
|
|
|
|
uint32_t ctrl_space = pci_read_field(pcidev, PCI_BAR0, 4);
|
|
|
|
pci_write_field(pcidev, PCI_BAR0, 4, 0xFFFFFFFF);
|
|
|
|
uint32_t ctrl_size = pci_read_field(pcidev, PCI_BAR0, 4);
|
|
|
|
ctrl_size = ~(ctrl_size & -15) + 1;
|
|
|
|
pci_write_field(pcidev, PCI_BAR0, 4, ctrl_space);
|
|
|
|
ctrl_space &= 0xFFFFFF00;
|
|
|
|
ctrl_regs = (uintptr_t)mmu_map_mmio_region(ctrl_space, ctrl_size);
|
|
|
|
|
|
|
|
lfb_resolution_impl = i965_modeset;
|
|
|
|
lfb_set_resolution(1440,900);
|
2022-07-25 03:51:04 +03:00
|
|
|
|
2023-05-20 23:43:08 +03:00
|
|
|
lfb_use_write_combining = 1;
|
2023-05-20 23:39:26 +03:00
|
|
|
|
2022-07-25 03:51:04 +03:00
|
|
|
/* Normally we don't clear the screen on mode set, but we should do it here */
|
|
|
|
memset(lfb_vid_memory, 0, lfb_memsize);
|
|
|
|
|
|
|
|
/* Redraw the boot logo; if we were loaded by userspace, it'll probably
|
|
|
|
* be overwritten pretty quickly by the compositor? But whatever... */
|
|
|
|
fbterm_reset();
|
|
|
|
fbterm_draw_logo();
|
|
|
|
|
|
|
|
/* Helpful to know why the console text got cleared */
|
|
|
|
dprintf("i965: video configured for %u x %u\n", lfb_resolution_x, lfb_resolution_y);
|
2021-06-03 15:02:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void find_intel(uint32_t device, uint16_t v, uint16_t d, void * extra) {
|
|
|
|
if (v == 0x8086 && d == 0x0046) {
|
|
|
|
setup_framebuffer(device);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-17 12:55:54 +03:00
|
|
|
static int i965_install(int argc, char * argv[]) {
|
|
|
|
if (args_present("noi965")) return -ENODEV;
|
2021-11-01 13:05:18 +03:00
|
|
|
if (!lfb_resolution_x) return -ENODEV;
|
2021-06-03 15:02:09 +03:00
|
|
|
pci_scan(find_intel, -1, NULL);
|
2021-07-17 12:55:54 +03:00
|
|
|
return 0;
|
2021-06-03 15:02:09 +03:00
|
|
|
}
|
2021-07-17 12:55:54 +03:00
|
|
|
|
|
|
|
static int fini(void) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Module metadata = {
|
|
|
|
.name = "i965",
|
|
|
|
.init = i965_install,
|
|
|
|
.fini = fini,
|
|
|
|
};
|
|
|
|
|