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/mmio-buffer.h>
10 #include <ddk/protocol/pci.h>
11 #include <ddk/protocol/pci-lib.h>
12 #include <hw/pci.h>
13 #include <zircon/pixelformat.h>
14 #include <zircon/process.h>
15 
16 #include "simple-display.h"
17 
18 #define DISPLAY_WIDTH 1024
19 #define DISPLAY_HEIGHT 768
20 #define DISPLAY_FORMAT ZX_PIXEL_FORMAT_RGB_565
21 
22 #define QEMU_VGA_VID (0x1234)
23 #define QEMU_VGA_DID (0x1111)
24 
25 #define bochs_vbe_dispi_read(base, reg) pcie_read16(base + (0x500 + (reg << 1)))
26 #define bochs_vbe_dispi_write(base, reg, val) pcie_write16(base + (0x500 + (reg << 1)), val)
27 
28 #define BOCHS_VBE_DISPI_ID 0x0
29 #define BOCHS_VBE_DISPI_XRES 0x1
30 #define BOCHS_VBE_DISPI_YRES 0x2
31 #define BOCHS_VBE_DISPI_BPP 0x3
32 #define BOCHS_VBE_DISPI_ENABLE 0x4
33 #define BOCHS_VBE_DISPI_BANK 0x5
34 #define BOCHS_VBE_DISPI_VIRT_WIDTH 0x6
35 #define BOCHS_VBE_DISPI_VIRT_HEIGHT 0x7
36 #define BOCHS_VBE_DISPI_X_OFFSET 0x8
37 #define BOCHS_VBE_DISPI_Y_OFFSET 0x9
38 #define BOCHS_VBE_DISPI_VIDEO_MEMORY_64K 0xa
39 
zx_display_format_to_bpp(zx_pixel_format_t format)40 static int zx_display_format_to_bpp(zx_pixel_format_t format) {
41     unsigned bpp = ZX_PIXEL_FORMAT_BYTES(format) * 8;
42     if (bpp == 0) {
43         // unknown
44         return -1;
45     } else {
46         return bpp;
47     }
48 }
49 
set_hw_mode(void * regs,uint16_t width,uint16_t height,zx_pixel_format_t format)50 static void set_hw_mode(void* regs, uint16_t width, uint16_t height, zx_pixel_format_t format) {
51     zxlogf(SPEW, "id: 0x%x\n", bochs_vbe_dispi_read(regs, BOCHS_VBE_DISPI_ID));
52 
53     int bpp = zx_display_format_to_bpp(format);
54     assert(bpp >= 0);
55 
56     bochs_vbe_dispi_write(regs, BOCHS_VBE_DISPI_ENABLE, 0);
57     bochs_vbe_dispi_write(regs, BOCHS_VBE_DISPI_BPP, bpp);
58     bochs_vbe_dispi_write(regs, BOCHS_VBE_DISPI_XRES, width);
59     bochs_vbe_dispi_write(regs, BOCHS_VBE_DISPI_YRES, height);
60     bochs_vbe_dispi_write(regs, BOCHS_VBE_DISPI_BANK, 0);
61     bochs_vbe_dispi_write(regs, BOCHS_VBE_DISPI_VIRT_WIDTH, width);
62     bochs_vbe_dispi_write(regs, BOCHS_VBE_DISPI_VIRT_HEIGHT, height);
63     bochs_vbe_dispi_write(regs, BOCHS_VBE_DISPI_X_OFFSET, 0);
64     bochs_vbe_dispi_write(regs, BOCHS_VBE_DISPI_Y_OFFSET, 0);
65     bochs_vbe_dispi_write(regs, BOCHS_VBE_DISPI_ENABLE, 0x41);
66 
67     zxlogf(SPEW, "bochs_vbe_set_hw_mode:\n");
68     zxlogf(SPEW, "     ID: 0x%x\n", bochs_vbe_dispi_read(regs, BOCHS_VBE_DISPI_ID));
69     zxlogf(SPEW, "   XRES: 0x%x\n", bochs_vbe_dispi_read(regs, BOCHS_VBE_DISPI_XRES));
70     zxlogf(SPEW, "   YRES: 0x%x\n", bochs_vbe_dispi_read(regs, BOCHS_VBE_DISPI_YRES));
71     zxlogf(SPEW, "    BPP: 0x%x\n", bochs_vbe_dispi_read(regs, BOCHS_VBE_DISPI_BPP));
72     zxlogf(SPEW, " ENABLE: 0x%x\n", bochs_vbe_dispi_read(regs, BOCHS_VBE_DISPI_ENABLE));
73     zxlogf(SPEW, "   BANK: 0x%x\n", bochs_vbe_dispi_read(regs, BOCHS_VBE_DISPI_BANK));
74     zxlogf(SPEW, "VWIDTH: 0x%x\n", bochs_vbe_dispi_read(regs, BOCHS_VBE_DISPI_VIRT_WIDTH));
75     zxlogf(SPEW, "VHEIGHT: 0x%x\n", bochs_vbe_dispi_read(regs, BOCHS_VBE_DISPI_VIRT_HEIGHT));
76     zxlogf(SPEW, "   XOFF: 0x%x\n", bochs_vbe_dispi_read(regs, BOCHS_VBE_DISPI_X_OFFSET));
77     zxlogf(SPEW, "   YOFF: 0x%x\n", bochs_vbe_dispi_read(regs, BOCHS_VBE_DISPI_Y_OFFSET));
78     zxlogf(SPEW, "    64K: 0x%x\n", bochs_vbe_dispi_read(regs, BOCHS_VBE_DISPI_VIDEO_MEMORY_64K));
79 }
80 
bochs_vbe_bind(void * ctx,zx_device_t * dev)81 static zx_status_t bochs_vbe_bind(void* ctx, zx_device_t* dev) {
82     pci_protocol_t pci;
83     zx_status_t status;
84 
85     if (device_get_protocol(dev, ZX_PROTOCOL_PCI, &pci))
86         return ZX_ERR_NOT_SUPPORTED;
87 
88     mmio_buffer_t mmio;
89     // map register window
90     status = pci_map_bar_buffer(&pci, 2u, ZX_CACHE_POLICY_UNCACHED_DEVICE, &mmio);
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(mmio.vaddr, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_FORMAT);
97 
98     mmio_buffer_release(&mmio);
99 
100     return bind_simple_pci_display(dev, "bochs_vbe", 0u,
101                                    DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_WIDTH, DISPLAY_FORMAT);
102 }
103 
104 static zx_driver_ops_t bochs_vbe_driver_ops = {
105     .version = DRIVER_OPS_VERSION,
106     .bind = bochs_vbe_bind,
107 };
108 
109 // clang-format off
110 ZIRCON_DRIVER_BEGIN(bochs_vbe, bochs_vbe_driver_ops, "zircon", "0.1", 3)
111     BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PCI),
112     BI_ABORT_IF(NE, BIND_PCI_VID, QEMU_VGA_VID),
113     BI_MATCH_IF(EQ, BIND_PCI_DID, QEMU_VGA_DID),
114 ZIRCON_DRIVER_END(bochs_vbe)
115