1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <ddk/binding.h>
6#include <ddk/debug.h>
7#include <ddk/device.h>
8#include <ddk/driver.h>
9#include <ddk/protocol/pci.h>
10#include <zircon/pixelformat.h>
11#include <zircon/process.h>
12
13#include "simple-display.h"
14
15#define DISPLAY_WIDTH 1024
16#define DISPLAY_HEIGHT 768
17#define DISPLAY_FORMAT ZX_PIXEL_FORMAT_RGB_565
18
19#define QEMU_VGA_VID (0x1234)
20#define QEMU_VGA_DID (0x1111)
21
22#define bochs_vbe_dispi_read(base, reg) pcie_read16(base + (0x500 + (reg << 1)))
23#define bochs_vbe_dispi_write(base, reg, val) pcie_write16(base + (0x500 + (reg << 1)), val)
24
25#define BOCHS_VBE_DISPI_ID 0x0
26#define BOCHS_VBE_DISPI_XRES 0x1
27#define BOCHS_VBE_DISPI_YRES 0x2
28#define BOCHS_VBE_DISPI_BPP 0x3
29#define BOCHS_VBE_DISPI_ENABLE 0x4
30#define BOCHS_VBE_DISPI_BANK 0x5
31#define BOCHS_VBE_DISPI_VIRT_WIDTH 0x6
32#define BOCHS_VBE_DISPI_VIRT_HEIGHT 0x7
33#define BOCHS_VBE_DISPI_X_OFFSET 0x8
34#define BOCHS_VBE_DISPI_Y_OFFSET 0x9
35#define BOCHS_VBE_DISPI_VIDEO_MEMORY_64K 0xa
36
37static int zx_display_format_to_bpp(zx_pixel_format_t format) {
38    unsigned bpp = ZX_PIXEL_FORMAT_BYTES(format) * 8;
39    if (bpp == 0) {
40        // unknown
41        return -1;
42    } else {
43        return bpp;
44    }
45}
46
47static void set_hw_mode(void* regs, uint16_t width, uint16_t height, zx_pixel_format_t format) {
48    zxlogf(SPEW, "id: 0x%x\n", bochs_vbe_dispi_read(regs, BOCHS_VBE_DISPI_ID));
49
50    int bpp = zx_display_format_to_bpp(format);
51    assert(bpp >= 0);
52
53    bochs_vbe_dispi_write(regs, BOCHS_VBE_DISPI_ENABLE, 0);
54    bochs_vbe_dispi_write(regs, BOCHS_VBE_DISPI_BPP, bpp);
55    bochs_vbe_dispi_write(regs, BOCHS_VBE_DISPI_XRES, width);
56    bochs_vbe_dispi_write(regs, BOCHS_VBE_DISPI_YRES, height);
57    bochs_vbe_dispi_write(regs, BOCHS_VBE_DISPI_BANK, 0);
58    bochs_vbe_dispi_write(regs, BOCHS_VBE_DISPI_VIRT_WIDTH, width);
59    bochs_vbe_dispi_write(regs, BOCHS_VBE_DISPI_VIRT_HEIGHT, height);
60    bochs_vbe_dispi_write(regs, BOCHS_VBE_DISPI_X_OFFSET, 0);
61    bochs_vbe_dispi_write(regs, BOCHS_VBE_DISPI_Y_OFFSET, 0);
62    bochs_vbe_dispi_write(regs, BOCHS_VBE_DISPI_ENABLE, 0x41);
63
64    zxlogf(SPEW, "bochs_vbe_set_hw_mode:\n");
65    zxlogf(SPEW, "     ID: 0x%x\n", bochs_vbe_dispi_read(regs, BOCHS_VBE_DISPI_ID));
66    zxlogf(SPEW, "   XRES: 0x%x\n", bochs_vbe_dispi_read(regs, BOCHS_VBE_DISPI_XRES));
67    zxlogf(SPEW, "   YRES: 0x%x\n", bochs_vbe_dispi_read(regs, BOCHS_VBE_DISPI_YRES));
68    zxlogf(SPEW, "    BPP: 0x%x\n", bochs_vbe_dispi_read(regs, BOCHS_VBE_DISPI_BPP));
69    zxlogf(SPEW, " ENABLE: 0x%x\n", bochs_vbe_dispi_read(regs, BOCHS_VBE_DISPI_ENABLE));
70    zxlogf(SPEW, "   BANK: 0x%x\n", bochs_vbe_dispi_read(regs, BOCHS_VBE_DISPI_BANK));
71    zxlogf(SPEW, "VWIDTH: 0x%x\n", bochs_vbe_dispi_read(regs, BOCHS_VBE_DISPI_VIRT_WIDTH));
72    zxlogf(SPEW, "VHEIGHT: 0x%x\n", bochs_vbe_dispi_read(regs, BOCHS_VBE_DISPI_VIRT_HEIGHT));
73    zxlogf(SPEW, "   XOFF: 0x%x\n", bochs_vbe_dispi_read(regs, BOCHS_VBE_DISPI_X_OFFSET));
74    zxlogf(SPEW, "   YOFF: 0x%x\n", bochs_vbe_dispi_read(regs, BOCHS_VBE_DISPI_Y_OFFSET));
75    zxlogf(SPEW, "    64K: 0x%x\n", bochs_vbe_dispi_read(regs, BOCHS_VBE_DISPI_VIDEO_MEMORY_64K));
76}
77
78static zx_status_t bochs_vbe_bind(void* ctx, zx_device_t* dev) {
79    pci_protocol_t pci;
80    zx_status_t status;
81
82    if (device_get_protocol(dev, ZX_PROTOCOL_PCI, &pci))
83        return ZX_ERR_NOT_SUPPORTED;
84
85    void* regs;
86    uint64_t regs_size;
87    zx_handle_t regs_handle;
88    // map register window
89    status = pci_map_bar(&pci, 2u, ZX_CACHE_POLICY_UNCACHED_DEVICE,
90                         &regs, &regs_size, &regs_handle);
91    if (status != ZX_OK) {
92        printf("bochs-vbe: failed to map pci config: %d\n", status);
93        return status;
94    }
95
96    set_hw_mode(regs, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_FORMAT);
97
98    zx_handle_close(regs_handle);
99    zx_vmar_unmap(zx_vmar_root_self(), (uintptr_t) regs, regs_size);
100
101    return bind_simple_pci_display(dev, "bochs_vbe", 0u,
102                                   DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_WIDTH, DISPLAY_FORMAT);
103}
104
105static zx_driver_ops_t bochs_vbe_driver_ops = {
106    .version = DRIVER_OPS_VERSION,
107    .bind = bochs_vbe_bind,
108};
109
110// clang-format off
111ZIRCON_DRIVER_BEGIN(bochs_vbe, bochs_vbe_driver_ops, "zircon", "0.1", 3)
112    BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PCI),
113    BI_ABORT_IF(NE, BIND_PCI_VID, QEMU_VGA_VID),
114    BI_MATCH_IF(EQ, BIND_PCI_DID, QEMU_VGA_DID),
115ZIRCON_DRIVER_END(bochs_vbe)
116