blob: 587ab094226e89caeb3879f67954118995d952e8 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <ddk/binding.h>
#include <ddk/device.h>
#include <ddk/driver.h>
#include <ddk/protocol/display.h>
#include <ddk/protocol/pci.h>
#include <hw/pci.h>
#include <assert.h>
#include <magenta/syscalls.h>
#include <magenta/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define QEMU_VGA_VID (0x1234)
#define QEMU_VGA_DID (0x1111)
#define TRACE 0
#if TRACE
#define xprintf(fmt...) printf(fmt)
#else
#define xprintf(fmt...) \
do { \
} while (0)
#endif
typedef struct bochs_vbe_device {
void* regs;
uint64_t regs_size;
mx_handle_t regs_handle;
void* framebuffer;
uint64_t framebuffer_size;
mx_handle_t framebuffer_handle;
mx_display_info_t info;
} bochs_vbe_device_t;
#define bochs_vbe_dispi_read(base, reg) pcie_read16(base + (0x500 + (reg << 1)))
#define bochs_vbe_dispi_write(base, reg, val) pcie_write16(base + (0x500 + (reg << 1)), val)
#define BOCHS_VBE_DISPI_ID 0x0
#define BOCHS_VBE_DISPI_XRES 0x1
#define BOCHS_VBE_DISPI_YRES 0x2
#define BOCHS_VBE_DISPI_BPP 0x3
#define BOCHS_VBE_DISPI_ENABLE 0x4
#define BOCHS_VBE_DISPI_BANK 0x5
#define BOCHS_VBE_DISPI_VIRT_WIDTH 0x6
#define BOCHS_VBE_DISPI_VIRT_HEIGHT 0x7
#define BOCHS_VBE_DISPI_X_OFFSET 0x8
#define BOCHS_VBE_DISPI_Y_OFFSET 0x9
#define BOCHS_VBE_DISPI_VIDEO_MEMORY_64K 0xa
static int mx_display_format_to_bpp(unsigned format) {
unsigned bpp;
switch (format) {
case MX_PIXEL_FORMAT_RGB_565:
bpp = 16;
break;
case MX_PIXEL_FORMAT_RGB_332:
bpp = 8;
break;
case MX_PIXEL_FORMAT_RGB_2220:
bpp = 6;
break;
case MX_PIXEL_FORMAT_ARGB_8888:
bpp = 32;
break;
case MX_PIXEL_FORMAT_RGB_x888:
bpp = 24;
break;
case MX_PIXEL_FORMAT_MONO_1:
bpp = 1;
break;
case MX_PIXEL_FORMAT_MONO_8:
bpp = 8;
break;
default:
// unsupported
bpp = -1;
break;
}
return bpp;
}
static void set_hw_mode(bochs_vbe_device_t* dev) {
xprintf("id: 0x%x\n", bochs_vbe_dispi_read(dev->regs, BOCHS_VBE_DISPI_ID));
int bpp = mx_display_format_to_bpp(dev->info.format);
assert(bpp >= 0);
bochs_vbe_dispi_write(dev->regs, BOCHS_VBE_DISPI_ENABLE, 0);
bochs_vbe_dispi_write(dev->regs, BOCHS_VBE_DISPI_BPP, bpp);
bochs_vbe_dispi_write(dev->regs, BOCHS_VBE_DISPI_XRES, dev->info.width);
bochs_vbe_dispi_write(dev->regs, BOCHS_VBE_DISPI_YRES, dev->info.height);
bochs_vbe_dispi_write(dev->regs, BOCHS_VBE_DISPI_BANK, 0);
bochs_vbe_dispi_write(dev->regs, BOCHS_VBE_DISPI_VIRT_WIDTH, dev->info.stride);
bochs_vbe_dispi_write(dev->regs, BOCHS_VBE_DISPI_VIRT_HEIGHT, dev->framebuffer_size / dev->info.stride);
bochs_vbe_dispi_write(dev->regs, BOCHS_VBE_DISPI_X_OFFSET, 0);
bochs_vbe_dispi_write(dev->regs, BOCHS_VBE_DISPI_Y_OFFSET, 0);
bochs_vbe_dispi_write(dev->regs, BOCHS_VBE_DISPI_ENABLE, 0x41);
mx_set_framebuffer(get_root_resource(), dev->framebuffer,
dev->framebuffer_size, dev->info.format,
dev->info.width, dev->info.height, dev->info.stride);
#if TRACE
xprintf("bochs_vbe_set_hw_mode:\n");
xprintf(" ID: 0x%x\n", bochs_vbe_dispi_read(dev->regs, BOCHS_VBE_DISPI_ID));
xprintf(" XRES: 0x%x\n", bochs_vbe_dispi_read(dev->regs, BOCHS_VBE_DISPI_XRES));
xprintf(" YRES: 0x%x\n", bochs_vbe_dispi_read(dev->regs, BOCHS_VBE_DISPI_YRES));
xprintf(" BPP: 0x%x\n", bochs_vbe_dispi_read(dev->regs, BOCHS_VBE_DISPI_BPP));
xprintf(" ENABLE: 0x%x\n", bochs_vbe_dispi_read(dev->regs, BOCHS_VBE_DISPI_ENABLE));
xprintf(" BANK: 0x%x\n", bochs_vbe_dispi_read(dev->regs, BOCHS_VBE_DISPI_BANK));
xprintf("VWIDTH: 0x%x\n", bochs_vbe_dispi_read(dev->regs, BOCHS_VBE_DISPI_VIRT_WIDTH));
xprintf("VHEIGHT: 0x%x\n", bochs_vbe_dispi_read(dev->regs, BOCHS_VBE_DISPI_VIRT_HEIGHT));
xprintf(" XOFF: 0x%x\n", bochs_vbe_dispi_read(dev->regs, BOCHS_VBE_DISPI_X_OFFSET));
xprintf(" YOFF: 0x%x\n", bochs_vbe_dispi_read(dev->regs, BOCHS_VBE_DISPI_Y_OFFSET));
xprintf(" 64K: 0x%x\n", bochs_vbe_dispi_read(dev->regs, BOCHS_VBE_DISPI_VIDEO_MEMORY_64K));
#endif
}
// implement display protocol
static mx_status_t bochs_vbe_set_mode(void* ctx, mx_display_info_t* info) {
assert(info);
bochs_vbe_device_t* vdev = ctx;
memcpy(&vdev->info, info, sizeof(mx_display_info_t));
set_hw_mode(vdev);
return MX_OK;
}
static mx_status_t bochs_vbe_get_mode(void* ctx, mx_display_info_t* info) {
assert(info);
bochs_vbe_device_t* vdev = ctx;
memcpy(info, &vdev->info, sizeof(mx_display_info_t));
return MX_OK;
}
static mx_status_t bochs_vbe_get_framebuffer(void* ctx, void** framebuffer) {
assert(framebuffer);
bochs_vbe_device_t* vdev = ctx;
(*framebuffer) = vdev->framebuffer;
return MX_OK;
}
static display_protocol_ops_t bochs_vbe_display_proto = {
.set_mode = bochs_vbe_set_mode,
.get_mode = bochs_vbe_get_mode,
.get_framebuffer = bochs_vbe_get_framebuffer,
};
// implement device protocol
static void bochs_vbe_release(void* ctx) {
bochs_vbe_device_t* vdev = ctx;
if (vdev->regs) {
mx_handle_close(vdev->regs_handle);
vdev->regs_handle = -1;
}
if (vdev->framebuffer) {
mx_handle_close(vdev->framebuffer_handle);
vdev->framebuffer_handle = -1;
}
free(vdev);
}
static mx_protocol_device_t bochs_vbe_device_proto = {
.version = DEVICE_OPS_VERSION,
.release = bochs_vbe_release,
};
// implement driver object:
static mx_status_t bochs_vbe_bind(void* ctx, mx_device_t* dev, void** cookie) {
pci_protocol_t pci;
mx_status_t status;
if (device_get_protocol(dev, MX_PROTOCOL_PCI, &pci))
return MX_ERR_NOT_SUPPORTED;
// map resources and initialize the device
bochs_vbe_device_t* device = calloc(1, sizeof(bochs_vbe_device_t));
if (!device)
return MX_ERR_NO_MEMORY;
// map register window
status = pci_map_resource(&pci, PCI_RESOURCE_BAR_2, MX_CACHE_POLICY_UNCACHED_DEVICE,
&device->regs, &device->regs_size,
&device->regs_handle);
if (status != MX_OK) {
printf("bochs-vbe: failed to map pci config: %d\n", status);
goto fail;
}
// map framebuffer window
status = pci_map_resource(&pci, PCI_RESOURCE_BAR_0, MX_CACHE_POLICY_WRITE_COMBINING,
&device->framebuffer,
&device->framebuffer_size,
&device->framebuffer_handle);
if (status != MX_OK) {
printf("bochs-vbe: failed to map pci config: %d\n", status);
goto fail;
}
device->info.format = MX_PIXEL_FORMAT_RGB_565;
device->info.width = 1024;
device->info.height = 768;
device->info.stride = 1024;
set_hw_mode(device);
// create and add the display (char) device
device_add_args_t args = {
.version = DEVICE_ADD_ARGS_VERSION,
.name = "bochs_vbe",
.ctx = device,
.ops = &bochs_vbe_device_proto,
.proto_id = MX_PROTOCOL_DISPLAY,
.proto_ops = &bochs_vbe_display_proto,
};
status = device_add(dev, &args, NULL);
if (status != MX_OK) {
goto fail;
}
xprintf("initialized bochs_vbe display driver, reg=0x%x regsize=0x%x fb=0x%x fbsize=0x%x\n",
device->regs, device->regs_size, device->framebuffer, device->framebuffer_size);
return MX_OK;
fail:
free(device);
return status;
}
static mx_driver_ops_t bochs_vbe_driver_ops = {
.version = DRIVER_OPS_VERSION,
.bind = bochs_vbe_bind,
};
// clang-format off
MAGENTA_DRIVER_BEGIN(bochs_vbe, bochs_vbe_driver_ops, "magenta", "0.1", 3)
BI_ABORT_IF(NE, BIND_PROTOCOL, MX_PROTOCOL_PCI),
BI_ABORT_IF(NE, BIND_PCI_VID, QEMU_VGA_VID),
BI_MATCH_IF(EQ, BIND_PCI_DID, QEMU_VGA_DID),
MAGENTA_DRIVER_END(bochs_vbe)