11.1Snia/* 21.1Snia * Copyright (c) 2025 The NetBSD Foundation, Inc. 31.1Snia * All rights reserved. 41.1Snia * 51.1Snia * Redistribution and use in source and binary forms, with or without 61.1Snia * modification, are permitted provided that the following conditions 71.1Snia * are met: 81.1Snia * 1. Redistributions of source code must retain the above copyright 91.1Snia * notice, this list of conditions and the following disclaimer. 101.1Snia * 2. Redistributions in binary form must reproduce the above copyright 111.1Snia * notice, this list of conditions and the following disclaimer in the 121.1Snia * documentation and/or other materials provided with the distribution. 131.1Snia * 141.1Snia * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 151.1Snia * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 161.1Snia * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 171.1Snia * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 181.1Snia * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 191.1Snia * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 201.1Snia * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 211.1Snia * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 221.1Snia * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 231.1Snia * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 241.1Snia */ 251.1Snia 261.1Snia/* 271.1Snia * Generic driver for Bochs Display Interface (DISPI) based devices: 281.1Snia * - Bochs VBE/VGA interface 291.1Snia * - QEMU standard VGA 301.1Snia * - QEMU virtio-vga series GPU 311.1Snia * 321.1Snia * This driver supports both MMIO and I/O port access methods. 331.1Snia */ 341.1Snia 351.1Snia#include <sys/cdefs.h> 361.1Snia__KERNEL_RCSID(0, "$NetBSD: bochsfb.c,v 1.1 2026/01/31 12:12:58 nia Exp $"); 371.1Snia 381.1Snia#include <sys/param.h> 391.1Snia#include <sys/systm.h> 401.1Snia#include <sys/kernel.h> 411.1Snia#include <sys/device.h> 421.1Snia#include <sys/bus.h> 431.1Snia#include <sys/endian.h> 441.1Snia#include <sys/kauth.h> 451.1Snia 461.1Snia#include <dev/pci/pcivar.h> 471.1Snia#include <dev/pci/pcireg.h> 481.1Snia#include <dev/pci/pcidevs.h> 491.1Snia#include <dev/pci/pciio.h> 501.1Snia 511.1Snia#include <dev/pci/bochsfbreg.h> 521.1Snia#include <dev/pci/bochsfbvar.h> 531.1Snia 541.1Snia#include <dev/videomode/videomode.h> 551.1Snia#include <dev/pci/wsdisplay_pci.h> 561.1Snia 571.1Snia#include "opt_wsemul.h" 581.1Snia 591.1Sniastatic int bochsfb_match(device_t, cfdata_t, void *); 601.1Sniastatic void bochsfb_attach(device_t, device_t, void *); 611.1Snia 621.1Sniastatic void bochsfb_write_dispi(struct bochsfb_softc *sc, uint16_t reg, 631.1Snia uint16_t val); 641.1Sniastatic uint16_t bochsfb_read_dispi(struct bochsfb_softc *sc, uint16_t reg); 651.1Sniastatic void bochsfb_write_vga(struct bochsfb_softc *sc, uint16_t reg, 661.1Snia uint8_t val); 671.1Sniastatic uint8_t bochsfb_read_vga(struct bochsfb_softc *sc, uint16_t reg); 681.1Sniastatic void bochsfb_set_blanking(struct bochsfb_softc *sc, int blank); 691.1Snia 701.1Sniastatic bool bochsfb_identify(struct bochsfb_softc *sc); 711.1Sniastatic int bochsfb_edid_mode(struct bochsfb_softc *sc); 721.1Sniastatic bool bochsfb_set_videomode(struct bochsfb_softc *sc); 731.1Snia 741.1Sniastatic paddr_t bochsfb_mmap(void *v, void *vs, off_t offset, int prot); 751.1Sniastatic int bochsfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, 761.1Snia struct lwp *l); 771.1Sniastatic void bochsfb_identify_screen(void *cookie, struct vcons_screen *scr, 781.1Snia int existing, long *defattr); 791.1Snia 801.1SniaCFATTACH_DECL_NEW(bochsfb, sizeof(struct bochsfb_softc), 811.1Sniabochsfb_match, bochsfb_attach, NULL, NULL); 821.1Snia 831.1Sniastruct wsdisplay_accessops bochsfb_accessops = { 841.1Snia bochsfb_ioctl, 851.1Snia bochsfb_mmap, 861.1Snia NULL, /* alloc_screen */ 871.1Snia NULL, /* free_screen */ 881.1Snia NULL, /* show_screen */ 891.1Snia NULL, /* load_font */ 901.1Snia NULL, /* pollc */ 911.1Snia NULL /* scroll */ 921.1Snia}; 931.1Snia 941.1Sniastatic int 951.1Sniabochsfb_match(device_t parent, cfdata_t match, void *aux) 961.1Snia{ 971.1Snia const struct pci_attach_args *pa = (const struct pci_attach_args *)aux; 981.1Snia 991.1Snia /* This is a unauthorized PCI ID */ 1001.1Snia if ((PCI_VENDOR(pa->pa_id) == 0x1234) && 1011.1Snia (PCI_PRODUCT(pa->pa_id) == 0x1111)) 1021.1Snia return 100; 1031.1Snia 1041.1Snia return 0; 1051.1Snia} 1061.1Snia 1071.1Sniastatic void 1081.1Sniabochsfb_attach(device_t parent, device_t self, void *aux) 1091.1Snia{ 1101.1Snia struct bochsfb_softc *sc = device_private(self); 1111.1Snia prop_dictionary_t dict = device_properties(self); 1121.1Snia struct wsemuldisplaydev_attach_args ws_aa; 1131.1Snia struct rasops_info *ri; 1141.1Snia const struct pci_attach_args *pa = aux; 1151.1Snia pcireg_t screg; 1161.1Snia bool is_console = false; 1171.1Snia long defattr; 1181.1Snia 1191.1Snia sc->sc_pc = pa->pa_pc; 1201.1Snia sc->sc_pcitag = pa->pa_tag; 1211.1Snia sc->sc_dev = self; 1221.1Snia sc->sc_pci_id = pa->pa_id; 1231.1Snia 1241.1Snia pci_aprint_devinfo(pa, NULL); 1251.1Snia prop_dictionary_get_bool(dict, "is_console", &is_console); 1261.1Snia 1271.1Snia /* 1281.1Snia * Map VGA I/O and memory space. 1291.1Snia * First try to map framebuffer memory 1301.1Snia */ 1311.1Snia if (pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_TYPE_MEM, 1321.1Snia BUS_SPACE_MAP_LINEAR, &sc->sc_memt, 1331.1Snia &sc->sc_fb_handle, &sc->sc_fb_addr, 1341.1Snia &sc->sc_fb_size) != 0) { 1351.1Snia aprint_error_dev(sc->sc_dev, "failed to map framebuffer memory\n"); 1361.1Snia return; 1371.1Snia } 1381.1Snia 1391.1Snia /* Try to map MMIO region for the DISPI interface */ 1401.1Snia if (pci_mapreg_map(pa, PCI_MAPREG_START + 8, PCI_MAPREG_TYPE_MEM, 1411.1Snia 0, &sc->sc_mmiot, &sc->sc_mmioh, &sc->sc_mmio_addr, 1421.1Snia &sc->sc_mmio_size) != 0) { 1431.1Snia 1441.1Snia aprint_normal_dev(sc->sc_dev, "MMIO BAR not available, using I/O ports\n"); 1451.1Snia sc->sc_has_mmio = false; 1461.1Snia 1471.1Snia /* I/O ports only exist if it's a VGA device*/ 1481.1Snia if (!(PCI_CLASS(pa->pa_class) == PCI_CLASS_DISPLAY && 1491.1Snia PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_DISPLAY_VGA)) { 1501.1Snia aprint_error_dev(sc->sc_dev, 1511.1Snia "DISPI I/O port not available\n"); 1521.1Snia return; 1531.1Snia } 1541.1Snia 1551.1Snia /* Map DISPI I/O ports as fallback */ 1561.1Snia if (bus_space_map(pa->pa_iot, VBE_DISPI_IOPORT_INDEX, 1571.1Snia 4, 0, &sc->sc_ioh_dispi) != 0) { 1581.1Snia aprint_error_dev(sc->sc_dev, 1591.1Snia "couldn't map DISPI I/O ports\n"); 1601.1Snia return; 1611.1Snia } 1621.1Snia 1631.1Snia /* Map I/O space for VGA and Bochs DISPI interface */ 1641.1Snia if (bus_space_map(pa->pa_iot, VGA_IO_START, VGA_IO_SIZE, 0, 1651.1Snia &sc->sc_ioh_vga) != 0) { 1661.1Snia aprint_error_dev(sc->sc_dev, "couldn't map VGA I/O space\n"); 1671.1Snia return; 1681.1Snia } 1691.1Snia sc->sc_iot = pa->pa_iot; 1701.1Snia } else { 1711.1Snia aprint_normal_dev(sc->sc_dev, "using MMIO for DISPI interface\n"); 1721.1Snia sc->sc_has_mmio = true; 1731.1Snia } 1741.1Snia 1751.1Snia /* Enable memory and I/O space */ 1761.1Snia screg = pci_conf_read(sc->sc_pc, sc->sc_pcitag, 1771.1Snia PCI_COMMAND_STATUS_REG); 1781.1Snia screg |= PCI_COMMAND_MEM_ENABLE; 1791.1Snia /* Avoid mess with legacy IO on secondary display */ 1801.1Snia if (!sc->sc_has_mmio) 1811.1Snia screg |= PCI_COMMAND_IO_ENABLE; 1821.1Snia pci_conf_write(sc->sc_pc, sc->sc_pcitag, PCI_COMMAND_STATUS_REG, screg); 1831.1Snia 1841.1Snia 1851.1Snia aprint_normal_dev(sc->sc_dev, "framebuffer at 0x%08lx, size %ld MB\n", 1861.1Snia (long)sc->sc_fb_addr, (long)sc->sc_fb_size / (1024 * 1024)); 1871.1Snia 1881.1Snia /* Initialize the display */ 1891.1Snia if (!bochsfb_identify(sc)) { 1901.1Snia aprint_error_dev(sc->sc_dev, "initialization failed\n"); 1911.1Snia return; 1921.1Snia } 1931.1Snia 1941.1Snia if (bochsfb_edid_mode(sc)) { 1951.1Snia /* No EDID data, use default resolution */ 1961.1Snia sc->sc_width = BOCHSFB_DEFAULT_WIDTH; 1971.1Snia sc->sc_height = BOCHSFB_DEFAULT_HEIGHT; 1981.1Snia } 1991.1Snia 2001.1Snia sc->sc_bpp = 32; /* 32 bbp */ 2011.1Snia sc->sc_linebytes = sc->sc_width * (sc->sc_bpp / 8); 2021.1Snia 2031.1Snia aprint_normal_dev(sc->sc_dev, "setting %dx%d %d bpp resolution\n", 2041.1Snia sc->sc_width, sc->sc_height, sc->sc_bpp); 2051.1Snia 2061.1Snia if (!bochsfb_set_videomode(sc)) { 2071.1Snia aprint_error_dev(sc->sc_dev, "couldn't set video mode\n"); 2081.1Snia return; 2091.1Snia } 2101.1Snia bochsfb_set_blanking(sc, WSDISPLAYIO_VIDEO_ON); 2111.1Snia 2121.1Snia sc->sc_defaultscreen_descr = (struct wsscreen_descr){ 2131.1Snia "default", 2141.1Snia 0, 0, 2151.1Snia NULL, 2161.1Snia 8, 16, 2171.1Snia WSSCREEN_WSCOLORS | WSSCREEN_HILIT, 2181.1Snia NULL 2191.1Snia }; 2201.1Snia sc->sc_screens[0] = &sc->sc_defaultscreen_descr; 2211.1Snia sc->sc_screenlist = (struct wsscreen_list){1, sc->sc_screens}; 2221.1Snia sc->sc_mode = WSDISPLAYIO_MODE_EMUL; 2231.1Snia 2241.1Snia vcons_init(&sc->vd, sc, &sc->sc_defaultscreen_descr, 2251.1Snia &bochsfb_accessops); 2261.1Snia sc->vd.init_screen = bochsfb_identify_screen; 2271.1Snia 2281.1Snia ri = &sc->sc_console_screen.scr_ri; 2291.1Snia 2301.1Snia if (is_console) { 2311.1Snia vcons_init_screen(&sc->vd, &sc->sc_console_screen, 1, 2321.1Snia &defattr); 2331.1Snia 2341.1Snia sc->sc_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC; 2351.1Snia vcons_redraw_screen(&sc->sc_console_screen); 2361.1Snia 2371.1Snia sc->sc_defaultscreen_descr.textops = &ri->ri_ops; 2381.1Snia sc->sc_defaultscreen_descr.capabilities = ri->ri_caps; 2391.1Snia sc->sc_defaultscreen_descr.nrows = ri->ri_rows; 2401.1Snia sc->sc_defaultscreen_descr.ncols = ri->ri_cols; 2411.1Snia 2421.1Snia wsdisplay_cnattach(&sc->sc_defaultscreen_descr, ri, 0, 0, 2431.1Snia defattr); 2441.1Snia vcons_replay_msgbuf(&sc->sc_console_screen); 2451.1Snia } else { 2461.1Snia if (sc->sc_console_screen.scr_ri.ri_rows == 0) { 2471.1Snia vcons_init_screen(&sc->vd, &sc->sc_console_screen, 1, 2481.1Snia &defattr); 2491.1Snia } else 2501.1Snia (*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr); 2511.1Snia } 2521.1Snia 2531.1Snia ws_aa.console = is_console; 2541.1Snia ws_aa.scrdata = &sc->sc_screenlist; 2551.1Snia ws_aa.accessops = &bochsfb_accessops; 2561.1Snia ws_aa.accesscookie = &sc->vd; 2571.1Snia 2581.1Snia config_found(sc->sc_dev, &ws_aa, wsemuldisplaydevprint, CFARGS_NONE); 2591.1Snia} 2601.1Snia 2611.1Sniastatic void 2621.1Sniabochsfb_identify_screen(void *cookie, struct vcons_screen *scr, int existing, 2631.1Snia long *defattr) 2641.1Snia{ 2651.1Snia struct bochsfb_softc *sc = cookie; 2661.1Snia struct rasops_info *ri = &scr->scr_ri; 2671.1Snia 2681.1Snia wsfont_init(); 2691.1Snia 2701.1Snia ri->ri_depth = sc->sc_bpp; 2711.1Snia ri->ri_width = sc->sc_width; 2721.1Snia ri->ri_height = sc->sc_height; 2731.1Snia ri->ri_stride = sc->sc_linebytes; 2741.1Snia ri->ri_flg = RI_CENTER; 2751.1Snia 2761.1Snia ri->ri_bits = bus_space_vaddr(sc->sc_memt, sc->sc_fb_handle); 2771.1Snia 2781.1Snia ri->ri_rnum = 8; 2791.1Snia ri->ri_gnum = 8; 2801.1Snia ri->ri_bnum = 8; 2811.1Snia ri->ri_rpos = 16; 2821.1Snia ri->ri_gpos = 8; 2831.1Snia ri->ri_bpos = 0; 2841.1Snia 2851.1Snia scr->scr_flags |= VCONS_DONT_READ; 2861.1Snia 2871.1Snia rasops_init(ri, 2881.1Snia ri->ri_height / 8, 2891.1Snia ri->ri_width / 8); 2901.1Snia 2911.1Snia ri->ri_caps = WSSCREEN_WSCOLORS; 2921.1Snia 2931.1Snia rasops_reconfig(ri, ri->ri_height / ri->ri_font->fontheight, 2941.1Snia ri->ri_width / ri->ri_font->fontwidth); 2951.1Snia 2961.1Snia ri->ri_hw = scr; 2971.1Snia} 2981.1Snia 2991.1Snia/* 3001.1Snia * Write to the VBE DISPI interface 3011.1Snia */ 3021.1Sniastatic void 3031.1Sniabochsfb_write_dispi(struct bochsfb_softc *sc, uint16_t reg, uint16_t val) 3041.1Snia{ 3051.1Snia if (sc->sc_has_mmio) { 3061.1Snia /* Use memory mapped I/O */ 3071.1Snia bus_space_write_2(sc->sc_mmiot, sc->sc_mmioh, 3081.1Snia BOCHSFB_MMIO_DISPI_OFFSET + reg * 2, val); 3091.1Snia } else { 3101.1Snia /* Use I/O ports */ 3111.1Snia bus_space_write_2(sc->sc_iot, sc->sc_ioh_dispi, 3121.1Snia VBE_DISPI_IOPORT_INDEX - VBE_DISPI_IOPORT_INDEX, reg); 3131.1Snia bus_space_write_2(sc->sc_iot, sc->sc_ioh_dispi, 3141.1Snia VBE_DISPI_IOPORT_DATA - VBE_DISPI_IOPORT_INDEX, val); 3151.1Snia } 3161.1Snia} 3171.1Snia 3181.1Snia/* 3191.1Snia* Read from the VBE DISPI interface 3201.1Snia*/ 3211.1Sniastatic uint16_t 3221.1Sniabochsfb_read_dispi(struct bochsfb_softc *sc, uint16_t reg) 3231.1Snia{ 3241.1Snia if (sc->sc_has_mmio) { 3251.1Snia /* Use memory mapped I/O */ 3261.1Snia return bus_space_read_2(sc->sc_mmiot, sc->sc_mmioh, 3271.1Snia BOCHSFB_MMIO_DISPI_OFFSET + reg * 2); 3281.1Snia } else { 3291.1Snia /* Use I/O ports */ 3301.1Snia bus_space_write_2(sc->sc_iot, sc->sc_ioh_dispi, 3311.1Snia VBE_DISPI_IOPORT_INDEX - VBE_DISPI_IOPORT_INDEX, reg); 3321.1Snia return bus_space_read_2(sc->sc_iot, sc->sc_ioh_dispi, 3331.1Snia VBE_DISPI_IOPORT_DATA - VBE_DISPI_IOPORT_INDEX); 3341.1Snia } 3351.1Snia} 3361.1Snia 3371.1Snia/* 3381.1Snia* Write to the VGA IO Ports 3391.1Snia*/ 3401.1Sniastatic void 3411.1Sniabochsfb_write_vga(struct bochsfb_softc *sc, uint16_t reg, uint8_t val) 3421.1Snia{ 3431.1Snia if (sc->sc_has_mmio) { 3441.1Snia /* Use memory mapped I/O */ 3451.1Snia bus_space_write_1(sc->sc_mmiot, sc->sc_mmioh, 3461.1Snia reg - VGA_IO_START + BOCHSFB_MMIO_VGA_OFFSET, 3471.1Snia val); 3481.1Snia return; 3491.1Snia } 3501.1Snia 3511.1Snia bus_space_write_1(sc->sc_iot, sc->sc_ioh_vga, reg, val); 3521.1Snia} 3531.1Snia 3541.1Snia/* 3551.1Snia* Read from the VGA IO Ports 3561.1Snia*/ 3571.1Sniastatic uint8_t 3581.1Sniabochsfb_read_vga(struct bochsfb_softc *sc, uint16_t reg) 3591.1Snia{ 3601.1Snia if (sc->sc_has_mmio) { 3611.1Snia /* Use memory mapped I/O */ 3621.1Snia return bus_space_read_1(sc->sc_mmiot, sc->sc_mmioh, 3631.1Snia reg - VGA_IO_START + BOCHSFB_MMIO_VGA_OFFSET); 3641.1Snia } 3651.1Snia 3661.1Snia return bus_space_read_1(sc->sc_iot, sc->sc_ioh_vga, reg); 3671.1Snia} 3681.1Snia 3691.1Snia/* 3701.1Snia* Identify the Bochs/QEMU display 3711.1Snia*/ 3721.1Sniastatic bool 3731.1Sniabochsfb_identify(struct bochsfb_softc *sc) 3741.1Snia{ 3751.1Snia /* Check for the Bochs display ID */ 3761.1Snia sc->sc_id = bochsfb_read_dispi(sc, VBE_DISPI_INDEX_ID); 3771.1Snia 3781.1Snia if ((sc->sc_id & 0xFFF0) != VBE_DISPI_ID0) { 3791.1Snia aprint_error_dev(sc->sc_dev, 3801.1Snia "invalid display ID 0x%04x\n", sc->sc_id); 3811.1Snia return false; 3821.1Snia } 3831.1Snia 3841.1Snia aprint_normal_dev(sc->sc_dev, "Bochs display ID 0x%04x found\n", 3851.1Snia sc->sc_id); 3861.1Snia 3871.1Snia return true; 3881.1Snia} 3891.1Snia 3901.1Sniastatic int bochsfb_edid_mode(struct bochsfb_softc *sc) 3911.1Snia{ 3921.1Snia int ret; 3931.1Snia 3941.1Snia if (!sc->sc_has_mmio) 3951.1Snia return -1; 3961.1Snia 3971.1Snia /* VirtIO VGA is not coming with EDID support */ 3981.1Snia if (PCI_VENDOR(sc->sc_pci_id) == PCI_VENDOR_QUMRANET) 3991.1Snia return -1; 4001.1Snia 4011.1Snia /* Read EDID data */ 4021.1Snia bus_space_read_region_1(sc->sc_mmiot, sc->sc_mmioh, 4031.1Snia BOCHSFB_MMIO_EDID_OFFSET, 4041.1Snia sc->edid_buf, 4051.1Snia BOCHSFB_MMIO_EDID_SIZE); 4061.1Snia 4071.1Snia /* Parse EDID data */ 4081.1Snia ret = edid_parse(sc->edid_buf, &sc->sc_ei); 4091.1Snia 4101.1Snia if (ret != 0) { 4111.1Snia aprint_normal_dev(sc->sc_dev, 4121.1Snia "failed to parse EDID data\n"); 4131.1Snia return ret; 4141.1Snia } 4151.1Snia 4161.1Snia /* Get the preferred mode */ 4171.1Snia if (!sc->sc_ei.edid_preferred_mode) { 4181.1Snia aprint_normal_dev(sc->sc_dev, 4191.1Snia "no preferred mode found in EDID data\n"); 4201.1Snia return -1; 4211.1Snia } 4221.1Snia 4231.1Snia /* Set the preferred mode */ 4241.1Snia sc->sc_width = sc->sc_ei.edid_preferred_mode->hdisplay; 4251.1Snia sc->sc_height = sc->sc_ei.edid_preferred_mode->vdisplay; 4261.1Snia 4271.1Snia return 0; 4281.1Snia} 4291.1Snia 4301.1Snia/* 4311.1Snia* Set video mode using the Bochs interface 4321.1Snia*/ 4331.1Sniastatic bool 4341.1Sniabochsfb_set_videomode(struct bochsfb_softc *sc) 4351.1Snia{ 4361.1Snia bochsfb_write_dispi(sc, VBE_DISPI_INDEX_ENABLE, 0); 4371.1Snia 4381.1Snia /* Set resolution and bit depth */ 4391.1Snia bochsfb_write_dispi(sc, VBE_DISPI_INDEX_BPP, sc->sc_bpp); 4401.1Snia bochsfb_write_dispi(sc, VBE_DISPI_INDEX_XRES, sc->sc_width); 4411.1Snia bochsfb_write_dispi(sc, VBE_DISPI_INDEX_YRES, sc->sc_height); 4421.1Snia bochsfb_write_dispi(sc, VBE_DISPI_INDEX_BANK, 0); 4431.1Snia bochsfb_write_dispi(sc, VBE_DISPI_INDEX_VIRT_WIDTH, sc->sc_width); 4441.1Snia bochsfb_write_dispi(sc, VBE_DISPI_INDEX_VIRT_HEIGHT, sc->sc_height); 4451.1Snia bochsfb_write_dispi(sc, VBE_DISPI_INDEX_X_OFFSET, 0); 4461.1Snia bochsfb_write_dispi(sc, VBE_DISPI_INDEX_Y_OFFSET, 0); 4471.1Snia 4481.1Snia /* Re-enable with linear frame buffer */ 4491.1Snia bochsfb_write_dispi(sc, VBE_DISPI_INDEX_ENABLE, 4501.1Snia VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED); 4511.1Snia 4521.1Snia return true; 4531.1Snia} 4541.1Snia 4551.1Sniastatic void 4561.1Sniabochsfb_set_blanking(struct bochsfb_softc *sc, int blank) 4571.1Snia{ 4581.1Snia bochsfb_write_vga(sc, 0x3C2, 0x01); 4591.1Snia (void)bochsfb_read_vga(sc, 0x3DA); 4601.1Snia 4611.1Snia if (blank == WSDISPLAYIO_VIDEO_OFF) { 4621.1Snia bochsfb_write_vga(sc, 0x3C0, 0x00); 4631.1Snia } else { 4641.1Snia bochsfb_write_vga(sc, 0x3C0, 0x20); 4651.1Snia } 4661.1Snia sc->sc_blank = blank; 4671.1Snia} 4681.1Snia 4691.1Sniastatic int 4701.1Sniabochsfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l) 4711.1Snia{ 4721.1Snia struct vcons_data *vd; 4731.1Snia struct bochsfb_softc *sc; 4741.1Snia struct wsdisplay_fbinfo *wsfbi; 4751.1Snia struct vcons_screen *ms; 4761.1Snia 4771.1Snia vd = v; 4781.1Snia sc = vd->cookie; 4791.1Snia ms = vd->active; 4801.1Snia 4811.1Snia switch (cmd) { 4821.1Snia case WSDISPLAYIO_GTYPE: 4831.1Snia *(u_int *)data = WSDISPLAY_TYPE_PCIMISC; 4841.1Snia return 0; 4851.1Snia 4861.1Snia case PCI_IOC_CFGREAD: 4871.1Snia case PCI_IOC_CFGWRITE: 4881.1Snia return pci_devioctl(sc->sc_pc, sc->sc_pcitag, 4891.1Snia cmd, data, flag, l); 4901.1Snia 4911.1Snia case WSDISPLAYIO_GET_BUSID: 4921.1Snia return wsdisplayio_busid_pci(sc->sc_dev, sc->sc_pc, 4931.1Snia sc->sc_pcitag, data); 4941.1Snia 4951.1Snia case WSDISPLAYIO_GINFO: 4961.1Snia if (ms == NULL) 4971.1Snia return ENODEV; 4981.1Snia 4991.1Snia wsfbi = (void *)data; 5001.1Snia wsfbi->height = ms->scr_ri.ri_height; 5011.1Snia wsfbi->width = ms->scr_ri.ri_width; 5021.1Snia wsfbi->depth = ms->scr_ri.ri_depth; 5031.1Snia wsfbi->cmsize = 0; /* No color map */ 5041.1Snia return 0; 5051.1Snia 5061.1Snia case WSDISPLAYIO_LINEBYTES: 5071.1Snia *(u_int *)data = sc->sc_linebytes; 5081.1Snia return 0; 5091.1Snia 5101.1Snia case WSDISPLAYIO_SMODE: 5111.1Snia { 5121.1Snia int new_mode = *(int *)data; 5131.1Snia if (new_mode != sc->sc_mode) { 5141.1Snia sc->sc_mode = new_mode; 5151.1Snia if (new_mode == WSDISPLAYIO_MODE_EMUL) { 5161.1Snia vcons_redraw_screen(ms); 5171.1Snia } 5181.1Snia } 5191.1Snia return 0; 5201.1Snia } 5211.1Snia case WSDISPLAYIO_GET_FBINFO: 5221.1Snia { 5231.1Snia struct wsdisplayio_fbinfo *fbi = data; 5241.1Snia struct rasops_info *ri; 5251.1Snia int ret; 5261.1Snia 5271.1Snia ri = &sc->vd.active->scr_ri; 5281.1Snia ret = wsdisplayio_get_fbinfo(ri, fbi); 5291.1Snia return ret; 5301.1Snia } 5311.1Snia case WSDISPLAYIO_GVIDEO: 5321.1Snia *(int *)data = sc->sc_blank; 5331.1Snia return 0; 5341.1Snia case WSDISPLAYIO_SVIDEO: 5351.1Snia bochsfb_set_blanking(sc, *(int *)data); 5361.1Snia return 0; 5371.1Snia case WSDISPLAYIO_GET_EDID: 5381.1Snia { 5391.1Snia struct wsdisplayio_edid_info *d = data; 5401.1Snia return wsdisplayio_get_edid(sc->sc_dev, d); 5411.1Snia } 5421.1Snia } 5431.1Snia 5441.1Snia 5451.1Snia return EPASSTHROUGH; 5461.1Snia} 5471.1Snia 5481.1Sniastatic paddr_t 5491.1Sniabochsfb_mmap(void *v, void *vs, off_t offset, int prot) 5501.1Snia{ 5511.1Snia struct vcons_data *vd; 5521.1Snia struct bochsfb_softc *sc; 5531.1Snia paddr_t pa; 5541.1Snia 5551.1Snia vd = v; 5561.1Snia sc = vd->cookie; 5571.1Snia 5581.1Snia if (sc->sc_mode == WSDISPLAYIO_MODE_DUMBFB) { 5591.1Snia if (offset < sc->sc_fb_size) { 5601.1Snia pa = bus_space_mmap(sc->sc_memt, sc->sc_fb_addr + offset, 0, 5611.1Snia prot, BUS_SPACE_MAP_LINEAR); 5621.1Snia return pa; 5631.1Snia } 5641.1Snia } else if (sc->sc_mode == WSDISPLAYIO_MODE_MAPPED) { 5651.1Snia if (kauth_authorize_machdep(kauth_cred_get(), 5661.1Snia KAUTH_MACHDEP_UNMANAGEDMEM, NULL, NULL, NULL, NULL) != 0) { 5671.1Snia aprint_error_dev(sc->sc_dev, "mmap() rejected.\n"); 5681.1Snia return -1; 5691.1Snia } 5701.1Snia 5711.1Snia if ((offset >= sc->sc_fb_addr) && 5721.1Snia (offset < sc->sc_fb_addr + sc->sc_fb_size)) { 5731.1Snia pa = bus_space_mmap(sc->sc_memt, offset, 0, prot, 5741.1Snia BUS_SPACE_MAP_LINEAR); 5751.1Snia return pa; 5761.1Snia } 5771.1Snia 5781.1Snia if (sc->sc_has_mmio && 5791.1Snia (offset >= sc->sc_mmio_addr) && 5801.1Snia (offset < sc->sc_mmio_addr + sc->sc_mmio_size)) { 5811.1Snia pa = bus_space_mmap(sc->sc_mmiot, offset, 0, prot, 5821.1Snia BUS_SPACE_MAP_LINEAR); 5831.1Snia return pa; 5841.1Snia } 5851.1Snia 5861.1Snia#ifdef PCI_MAGIC_IO_RANGE 5871.1Snia /* allow mapping of IO space */ 5881.1Snia if ((offset >= PCI_MAGIC_IO_RANGE) && 5891.1Snia (offset < PCI_MAGIC_IO_RANGE + 0x10000)) { 5901.1Snia pa = bus_space_mmap(sc->sc_iot, 5911.1Snia offset - PCI_MAGIC_IO_RANGE, 0, prot, 0); 5921.1Snia return pa; 5931.1Snia } 5941.1Snia#endif 5951.1Snia } 5961.1Snia 5971.1Snia return -1; 5981.1Snia} 599