Index: sys/dev/drm2/drm_fb_helper.c =================================================================== --- sys/dev/drm2/drm_fb_helper.c +++ sys/dev/drm2/drm_fb_helper.c @@ -69,6 +69,21 @@ return (0); } +static int +vt_kms_reset(void *arg) +{ + struct vt_kms_softc *sc; + device_t vga; + int ret; + + sc = (struct vt_kms_softc *)arg; + + vga = device_get_parent(sc->fb_helper->dev->device); + ret = vga_pci_repost(vga); + + return (ret); +} + static DRM_LIST_HEAD(kernel_fb_helper_list); /* simple single crtc case helper function */ @@ -1024,6 +1039,7 @@ info->fb_stride = fb_helper->fb->pitches[0]; info->fb_priv = sc; info->enter = &vt_kms_postswitch; + info->reset = &vt_kms_reset; /* set the fb pointer */ for (i = 0; i < fb_helper->crtc_count; i++) { @@ -1036,10 +1052,12 @@ kdev = fb_helper->dev->device; fbd = device_add_child(kdev, "fbd", device_get_unit(kdev)); - if (fbd != NULL) + if (fbd != NULL) { + info->fbd_dev = fbd; ret = device_probe_and_attach(fbd); - else + } else { ret = ENODEV; + } #ifdef DEV_VT if (ret != 0) DRM_ERROR("Failed to attach fbd device: %d\n", ret); Index: sys/dev/drm2/radeon/radeon_fb.c =================================================================== --- sys/dev/drm2/radeon/radeon_fb.c +++ sys/dev/drm2/radeon/radeon_fb.c @@ -291,6 +291,8 @@ if (rfbdev->helper.fbdev) { info = rfbdev->helper.fbdev; + vga_pci_repost(device_get_parent(dev->device)); + free(info->fb_priv, DRM_MEM_KMS); free(info, DRM_MEM_KMS); } Index: sys/dev/fb/fbd.c =================================================================== --- sys/dev/fb/fbd.c +++ sys/dev/fb/fbd.c @@ -51,6 +51,8 @@ #include #include +#include + #include "fb_if.h" LIST_HEAD(fb_list_head_t, fb_list_entry) fb_list_head = @@ -262,6 +264,8 @@ LIST_FOREACH_SAFE(entry, &fb_list_head, fb_list, tmp) { if (entry->fb_info == info) { LIST_REMOVE(entry, fb_list); + if (LIST_EMPTY(&fb_list_head)) + vt_fb_detach(info); free(entry, M_DEVBUF); return (0); } Index: sys/dev/pci/pcivar.h =================================================================== --- sys/dev/pci/pcivar.h +++ sys/dev/pci/pcivar.h @@ -565,5 +565,6 @@ int vga_pci_is_boot_display(device_t dev); void * vga_pci_map_bios(device_t dev, size_t *size); void vga_pci_unmap_bios(device_t dev, void *bios); +int vga_pci_repost(device_t dev); #endif /* _PCIVAR_H_ */ Index: sys/dev/pci/vga_pci.c =================================================================== --- sys/dev/pci/vga_pci.c +++ sys/dev/pci/vga_pci.c @@ -51,6 +51,8 @@ #include #include +#include /* To re-POST the card. */ + struct vga_resource { struct resource *vr_res; int vr_refs; @@ -194,6 +196,35 @@ vr->vr_res); } +int +vga_pci_repost(device_t dev) +{ +#if defined(__amd64__) || defined(__i386__) + x86regs_t regs; + + if (!vga_pci_is_boot_display(dev)) + return (EINVAL); + + if (x86bios_get_orm(VGA_PCI_BIOS_SHADOW_ADDR) == NULL) + return (ENOTSUP); + + x86bios_init_regs(®s); + + regs.R_AH = pci_get_bus(dev); + regs.R_AL = (pci_get_slot(dev) << 3) | (pci_get_function(dev) & 0x07); + regs.R_DL = 0x80; + + x86bios_call(®s, X86BIOS_PHYSTOSEG(VGA_PCI_BIOS_SHADOW_ADDR + 3), + X86BIOS_PHYSTOOFF(VGA_PCI_BIOS_SHADOW_ADDR + 3)); + + x86bios_get_intr(0x10); + + return (0); +#else + return (ENOTSUP); +#endif +} + static int vga_pci_probe(device_t dev) { Index: sys/dev/vt/hw/fb/vt_fb.h =================================================================== --- sys/dev/vt/hw/fb/vt_fb.h +++ sys/dev/vt/hw/fb/vt_fb.h @@ -35,8 +35,10 @@ int vt_fb_attach(struct fb_info *info); void vt_fb_resume(void); void vt_fb_suspend(void); +int vt_fb_detach(struct fb_info *info); vd_init_t vt_fb_init; +vd_reset_t vt_fb_reset; vd_blank_t vt_fb_blank; vd_bitblt_text_t vt_fb_bitblt_text; vd_bitblt_bmp_t vt_fb_bitblt_bitmap; Index: sys/dev/vt/hw/fb/vt_fb.c =================================================================== --- sys/dev/vt/hw/fb/vt_fb.c +++ sys/dev/vt/hw/fb/vt_fb.c @@ -47,6 +47,7 @@ static struct vt_driver vt_fb_driver = { .vd_name = "fb", .vd_init = vt_fb_init, + .vd_reset = vt_fb_reset, .vd_blank = vt_fb_blank, .vd_bitblt_text = vt_fb_bitblt_text, .vd_bitblt_bmp = vt_fb_bitblt_bitmap, @@ -439,6 +440,21 @@ } int +vt_fb_reset(struct vt_device *vd) +{ + struct fb_info *info; + int err; + + info = vd->vd_softc; + + err = 0; + if (info->reset != NULL) + err = info->reset(info->fb_priv); + + return (err); +} + +int vt_fb_attach(struct fb_info *info) { @@ -447,6 +463,15 @@ return (0); } +int +vt_fb_detach(struct fb_info *info) +{ + + vt_deallocate(&vt_fb_driver, info); + + return (0); +} + void vt_fb_resume(void) { Index: sys/dev/vt/vt.h =================================================================== --- sys/dev/vt/vt.h +++ sys/dev/vt/vt.h @@ -95,9 +95,10 @@ struct vt_driver; -void vt_allocate(struct vt_driver *, void *); +void vt_allocate(const struct vt_driver *, void *); void vt_resume(void); void vt_suspend(void); +void vt_deallocate(const struct vt_driver *, void *); typedef unsigned int vt_axis_t; @@ -126,6 +127,8 @@ struct vt_window *vd_markedwin; /* (?) Copy/paste buf owner. */ const struct vt_driver *vd_driver; /* (c) Graphics driver. */ void *vd_softc; /* (u) Driver data. */ + const struct vt_driver *vd_old_driver; /* (?) Previous driver. */ + void *vd_old_softc; /* (?) Previous driver data. */ #ifndef SC_NO_CUTPASTE struct vt_mouse_cursor *vd_mcursor; /* (?) Cursor bitmap. */ term_color_t vd_mcursor_fg; /* (?) Cursor fg color. */ @@ -292,6 +295,7 @@ typedef int vd_init_t(struct vt_device *vd); typedef int vd_probe_t(struct vt_device *vd); +typedef int vd_reset_t(struct vt_device *vd); typedef void vd_postswitch_t(struct vt_device *vd); typedef void vd_blank_t(struct vt_device *vd, term_color_t color); typedef void vd_bitblt_text_t(struct vt_device *vd, const struct vt_window *vw, @@ -312,6 +316,7 @@ /* Console attachment. */ vd_probe_t *vd_probe; vd_init_t *vd_init; + vd_reset_t *vd_reset; /* Drawing. */ vd_blank_t *vd_blank; Index: sys/dev/vt/vt_core.c =================================================================== --- sys/dev/vt/vt_core.c +++ sys/dev/vt/vt_core.c @@ -157,6 +157,8 @@ static struct vt_device vt_consdev = { .vd_driver = NULL, .vd_softc = NULL, + .vd_old_driver = NULL, + .vd_old_softc = NULL, .vd_flags = VDF_INVALID, .vd_windows = { [VT_CONSWINDOW] = &vt_conswindow, }, .vd_curwindow = &vt_conswindow, @@ -2178,31 +2180,11 @@ } } -void -vt_allocate(struct vt_driver *drv, void *softc) +static void +vt_replace(const struct vt_driver *drv, void *softc, int is_downgrade) { struct vt_device *vd; - if (!vty_enabled(VTY_VT)) - return; - - if (main_vd->vd_driver == NULL) { - main_vd->vd_driver = drv; - printf("VT: initialize with new VT driver \"%s\".\n", - drv->vd_name); - } else { - /* - * Check if have rights to replace current driver. For example: - * it is bad idea to replace KMS driver with generic VGA one. - */ - if (drv->vd_priority <= main_vd->vd_driver->vd_priority) { - printf("VT: Driver priority %d too low. Current %d\n ", - drv->vd_priority, main_vd->vd_driver->vd_priority); - return; - } - printf("VT: Replacing driver \"%s\" with new \"%s\".\n", - main_vd->vd_driver->vd_name, drv->vd_name); - } vd = main_vd; VT_LOCK(vd); @@ -2222,8 +2204,20 @@ */ vd->vd_flags &= ~VDF_TEXTMODE; + if (!is_downgrade) { + vd->vd_old_driver = vd->vd_driver; + vd->vd_old_softc = vd->vd_softc; + } else { + if (vd->vd_driver->vd_reset) + vd->vd_driver->vd_reset(vd); + + vd->vd_old_driver = NULL; + vd->vd_old_softc = NULL; + } + vd->vd_driver = drv; vd->vd_softc = softc; + vd->vd_driver->vd_init(vd); VT_UNLOCK(vd); @@ -2250,6 +2244,50 @@ } void +vt_allocate(const struct vt_driver *drv, void *softc) +{ + + if (!vty_enabled(VTY_VT)) + return; + + if (main_vd->vd_driver == NULL) { + main_vd->vd_driver = drv; + printf("VT: initialize with new VT driver \"%s\".\n", + drv->vd_name); + } else { + /* + * Check if have rights to replace current driver. For example: + * it is bad idea to replace KMS driver with generic VGA one. + */ + if (drv->vd_priority <= main_vd->vd_driver->vd_priority) { + printf("VT: Driver priority %d too low. Current %d\n ", + drv->vd_priority, main_vd->vd_driver->vd_priority); + return; + } + printf("VT: Replacing driver \"%s\" with new \"%s\".\n", + main_vd->vd_driver->vd_name, drv->vd_name); + } + + vt_replace(drv, softc, 0); +} + +void +vt_deallocate(const struct vt_driver *drv, void *softc) +{ + + if (!vty_enabled(VTY_VT)) + return; + + if (main_vd->vd_old_driver == NULL) + return; + + printf("VT: Switching back from \"%s\" to \"%s\".\n", + main_vd->vd_driver->vd_name, main_vd->vd_old_driver->vd_name); + + vt_replace(main_vd->vd_old_driver, main_vd->vd_old_softc, 1); +} + +void vt_suspend() { Index: sys/sys/fbio.h =================================================================== --- sys/sys/fbio.h +++ sys/sys/fbio.h @@ -115,6 +115,7 @@ typedef int fb_enter_t(void *priv); typedef int fb_leave_t(void *priv); +typedef int fb_reset_t(void *priv); struct fb_info { /* Raw copy of fbtype. Do not change. */ @@ -127,8 +128,11 @@ struct cdev *fb_cdev; + device_t fbd_dev; /* "fbd" device. */ + fb_enter_t *enter; fb_leave_t *leave; + fb_reset_t *reset; intptr_t fb_pbase; /* For FB mmap. */ intptr_t fb_vbase; /* if NULL, use fb_write/fb_read. */