bochsfb.c revision 1.1
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