Author: dumbbell
Date: Sun Mar  1 12:54:22 2015
New Revision: 279488
URL: https://svnweb.freebsd.org/changeset/base/279488

Log:
  vt(4): Add support to "downgrade" from eg. vt_fb to vt_vga
  
  The main purpose of this feature is to be able to unload a KMS driver.
  
  When going back from the current vt(4) backend to the previous backend,
  the previous backend is reinitialized with the special VDF_DOWNGRADE
  flag set. Then the current driver is terminated with the new "vd_fini"
  callback.
  
  In the case of vt_fb and vt_vga, this allows the former to pass the
  vgapci device vt_fb used to vt_vga so the device can be rePOSTed.
  
  Differential Revision:        https://reviews.freebsd.org/D687

Modified:
  head/sys/dev/drm2/drm_fb_helper.c
  head/sys/dev/drm2/radeon/radeon_fb.c
  head/sys/dev/fb/fbd.c
  head/sys/dev/vt/hw/fb/vt_fb.c
  head/sys/dev/vt/hw/fb/vt_fb.h
  head/sys/dev/vt/hw/vga/vt_vga.c
  head/sys/dev/vt/vt.h
  head/sys/dev/vt/vt_core.c
  head/sys/sys/fbio.h

Modified: head/sys/dev/drm2/drm_fb_helper.c
==============================================================================
--- head/sys/dev/drm2/drm_fb_helper.c   Sun Mar  1 12:47:36 2015        
(r279487)
+++ head/sys/dev/drm2/drm_fb_helper.c   Sun Mar  1 12:54:22 2015        
(r279488)
@@ -937,19 +937,21 @@ int drm_fb_helper_single_fb_probe(struct
        info->fb_priv = sc;
        info->enter = &vt_kms_postswitch;
 
+       kdev = fb_helper->dev->device;
+       info->fb_video_dev = device_get_parent(kdev);
+
        /* set the fb pointer */
        for (i = 0; i < fb_helper->crtc_count; i++) {
                fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb;
        }
 
        if (new_fb) {
-               device_t fbd;
                int ret;
 
-               kdev = fb_helper->dev->device;
-               fbd = device_add_child(kdev, "fbd", device_get_unit(kdev));
-               if (fbd != NULL) 
-                       ret = device_probe_and_attach(fbd);
+               info->fb_fbd_dev = device_add_child(kdev, "fbd",
+                   device_get_unit(kdev));
+               if (info->fb_fbd_dev != NULL)
+                       ret = device_probe_and_attach(info->fb_fbd_dev);
                else
                        ret = ENODEV;
 #ifdef DEV_VT

Modified: head/sys/dev/drm2/radeon/radeon_fb.c
==============================================================================
--- head/sys/dev/drm2/radeon/radeon_fb.c        Sun Mar  1 12:47:36 2015        
(r279487)
+++ head/sys/dev/drm2/radeon/radeon_fb.c        Sun Mar  1 12:54:22 2015        
(r279488)
@@ -291,6 +291,8 @@ static int radeon_fbdev_destroy(struct d
 
        if (rfbdev->helper.fbdev) {
                info = rfbdev->helper.fbdev;
+               if (info->fb_fbd_dev != NULL)
+                       device_delete_child(dev->device, info->fb_fbd_dev);
                free(info->fb_priv, DRM_MEM_KMS);
                free(info, DRM_MEM_KMS);
        }

Modified: head/sys/dev/fb/fbd.c
==============================================================================
--- head/sys/dev/fb/fbd.c       Sun Mar  1 12:47:36 2015        (r279487)
+++ head/sys/dev/fb/fbd.c       Sun Mar  1 12:54:22 2015        (r279488)
@@ -263,6 +263,8 @@ fbd_unregister(struct fb_info* info)
        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);
                }

Modified: head/sys/dev/vt/hw/fb/vt_fb.c
==============================================================================
--- head/sys/dev/vt/hw/fb/vt_fb.c       Sun Mar  1 12:47:36 2015        
(r279487)
+++ head/sys/dev/vt/hw/fb/vt_fb.c       Sun Mar  1 12:54:22 2015        
(r279488)
@@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$");
 static struct vt_driver vt_fb_driver = {
        .vd_name = "fb",
        .vd_init = vt_fb_init,
+       .vd_fini = vt_fb_fini,
        .vd_blank = vt_fb_blank,
        .vd_bitblt_text = vt_fb_bitblt_text,
        .vd_bitblt_bmp = vt_fb_bitblt_bitmap,
@@ -419,6 +420,7 @@ vt_fb_init(struct vt_device *vd)
        info = vd->vd_softc;
        vd->vd_height = info->fb_height;
        vd->vd_width = info->fb_width;
+       vd->vd_video_dev = info->fb_video_dev;
 
        if (info->fb_size == 0)
                return (CN_DEAD);
@@ -442,6 +444,13 @@ vt_fb_init(struct vt_device *vd)
        return (CN_INTERNAL);
 }
 
+void
+vt_fb_fini(struct vt_device *vd, void *softc)
+{
+
+       vd->vd_video_dev = NULL;
+}
+
 int
 vt_fb_attach(struct fb_info *info)
 {
@@ -451,6 +460,15 @@ vt_fb_attach(struct fb_info *info)
        return (0);
 }
 
+int
+vt_fb_detach(struct fb_info *info)
+{
+
+       vt_deallocate(&vt_fb_driver, info);
+
+       return (0);
+}
+
 void
 vt_fb_suspend(struct vt_device *vd)
 {

Modified: head/sys/dev/vt/hw/fb/vt_fb.h
==============================================================================
--- head/sys/dev/vt/hw/fb/vt_fb.h       Sun Mar  1 12:47:36 2015        
(r279487)
+++ head/sys/dev/vt/hw/fb/vt_fb.h       Sun Mar  1 12:54:22 2015        
(r279488)
@@ -35,8 +35,10 @@
 int vt_fb_attach(struct fb_info *info);
 void vt_fb_resume(struct vt_device *vd);
 void vt_fb_suspend(struct vt_device *vd);
+int vt_fb_detach(struct fb_info *info);
 
 vd_init_t              vt_fb_init;
+vd_fini_t              vt_fb_fini;
 vd_blank_t             vt_fb_blank;
 vd_bitblt_text_t       vt_fb_bitblt_text;
 vd_bitblt_bmp_t                vt_fb_bitblt_bitmap;

Modified: head/sys/dev/vt/hw/vga/vt_vga.c
==============================================================================
--- head/sys/dev/vt/hw/vga/vt_vga.c     Sun Mar  1 12:47:36 2015        
(r279487)
+++ head/sys/dev/vt/hw/vga/vt_vga.c     Sun Mar  1 12:54:22 2015        
(r279488)
@@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$");
 
 #include <dev/vt/vt.h>
 #include <dev/vt/hw/vga/vt_vga_reg.h>
+#include <dev/pci/pcivar.h>
 
 #include <machine/bus.h>
 
@@ -1213,6 +1214,9 @@ vga_init(struct vt_device *vd)
        sc = vd->vd_softc;
        textmode = 0;
 
+       if (vd->vd_flags & VDF_DOWNGRADE && vd->vd_video_dev != NULL)
+               vga_pci_repost(vd->vd_video_dev);
+
 #if defined(__amd64__) || defined(__i386__)
        sc->vga_fb_tag = X86_BUS_SPACE_MEM;
        sc->vga_fb_handle = KERNBASE + VGA_MEM_BASE;

Modified: head/sys/dev/vt/vt.h
==============================================================================
--- head/sys/dev/vt/vt.h        Sun Mar  1 12:47:36 2015        (r279487)
+++ head/sys/dev/vt/vt.h        Sun Mar  1 12:54:22 2015        (r279488)
@@ -89,7 +89,8 @@ SYSCTL_INT(_kern_vt, OID_AUTO, _name, CT
 
 struct vt_driver;
 
-void vt_allocate(struct vt_driver *, void *);
+void vt_allocate(const struct vt_driver *, void *);
+void vt_deallocate(const struct vt_driver *, void *);
 
 typedef unsigned int   vt_axis_t;
 
@@ -124,6 +125,9 @@ struct vt_device {
        struct vt_pastebuf       vd_pastebuf;   /* (?) Copy/paste buf. */
        const struct vt_driver  *vd_driver;     /* (c) Graphics driver. */
        void                    *vd_softc;      /* (u) Driver data. */
+       const struct vt_driver  *vd_prev_driver;/* (?) Previous driver. */
+       void                    *vd_prev_softc; /* (?) Previous driver data. */
+       device_t                 vd_video_dev;  /* (?) Video adapter. */
 #ifndef SC_NO_CUTPASTE
        struct vt_mouse_cursor  *vd_mcursor;    /* (?) Cursor bitmap. */
        term_color_t             vd_mcursor_fg; /* (?) Cursor fg color. */
@@ -150,6 +154,7 @@ struct vt_device {
 #define        VDF_INITIALIZED 0x20    /* vtterm_cnprobe already done. */
 #define        VDF_MOUSECURSOR 0x40    /* Mouse cursor visible. */
 #define        VDF_QUIET_BELL  0x80    /* Disable bell. */
+#define        VDF_DOWNGRADE   0x8000  /* The driver is being downgraded. */
        int                      vd_keyboard;   /* (G) Keyboard index. */
        unsigned int             vd_kbstate;    /* (?) Device unit. */
        unsigned int             vd_unit;       /* (c) Device unit. */
@@ -301,6 +306,7 @@ struct vt_window {
 
 typedef int vd_init_t(struct vt_device *vd);
 typedef int vd_probe_t(struct vt_device *vd);
+typedef void vd_fini_t(struct vt_device *vd, void *softc);
 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,
@@ -323,6 +329,7 @@ struct vt_driver {
        /* Console attachment. */
        vd_probe_t      *vd_probe;
        vd_init_t       *vd_init;
+       vd_fini_t       *vd_fini;
 
        /* Drawing. */
        vd_blank_t      *vd_blank;

Modified: head/sys/dev/vt/vt_core.c
==============================================================================
--- head/sys/dev/vt/vt_core.c   Sun Mar  1 12:47:36 2015        (r279487)
+++ head/sys/dev/vt/vt_core.c   Sun Mar  1 12:54:22 2015        (r279488)
@@ -180,6 +180,8 @@ static struct vt_window     vt_conswindow;
 static struct vt_device        vt_consdev = {
        .vd_driver = NULL,
        .vd_softc = NULL,
+       .vd_prev_driver = NULL,
+       .vd_prev_softc = NULL,
        .vd_flags = VDF_INVALID,
        .vd_windows = { [VT_CONSWINDOW] =  &vt_conswindow, },
        .vd_curwindow = &vt_conswindow,
@@ -2598,31 +2600,11 @@ vt_resize(struct vt_device *vd)
        }
 }
 
-void
-vt_allocate(struct vt_driver *drv, void *softc)
+static void
+vt_replace_backend(const struct vt_driver *drv, void *softc)
 {
        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;
 
        if (vd->vd_flags & VDF_ASYNC) {
@@ -2644,9 +2626,44 @@ vt_allocate(struct vt_driver *drv, void 
        VT_LOCK(vd);
        vd->vd_flags &= ~VDF_TEXTMODE;
 
-       vd->vd_driver = drv;
-       vd->vd_softc = softc;
-       vd->vd_driver->vd_init(vd);
+       if (drv != NULL) {
+               /*
+                * We want to upgrade from the current driver to the
+                * given driver.
+                */
+
+               vd->vd_prev_driver = vd->vd_driver;
+               vd->vd_prev_softc = vd->vd_softc;
+               vd->vd_driver = drv;
+               vd->vd_softc = softc;
+
+               vd->vd_driver->vd_init(vd);
+       } else if (vd->vd_prev_driver != NULL && vd->vd_prev_softc != NULL) {
+               /*
+                * No driver given: we want to downgrade to the previous
+                * driver.
+                */
+               const struct vt_driver *old_drv;
+               void *old_softc;
+
+               old_drv = vd->vd_driver;
+               old_softc = vd->vd_softc;
+
+               vd->vd_driver = vd->vd_prev_driver;
+               vd->vd_softc = vd->vd_prev_softc;
+               vd->vd_prev_driver = NULL;
+               vd->vd_prev_softc = NULL;
+
+               vd->vd_flags |= VDF_DOWNGRADE;
+
+               vd->vd_driver->vd_init(vd);
+
+               if (old_drv->vd_fini)
+                       old_drv->vd_fini(vd, old_softc);
+
+               vd->vd_flags &= ~VDF_DOWNGRADE;
+       }
+
        VT_UNLOCK(vd);
 
        /* Update windows sizes and initialize last items. */
@@ -2692,6 +2709,52 @@ vt_resume_handler(void *priv)
 }
 
 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_backend(drv, softc);
+}
+
+void
+vt_deallocate(const struct vt_driver *drv, void *softc)
+{
+
+       if (!vty_enabled(VTY_VT))
+               return;
+
+       if (main_vd->vd_prev_driver == NULL ||
+           main_vd->vd_driver != drv ||
+           main_vd->vd_softc != softc)
+               return;
+
+       printf("VT: Switching back from \"%s\" to \"%s\".\n",
+           main_vd->vd_driver->vd_name, main_vd->vd_prev_driver->vd_name);
+
+       vt_replace_backend(NULL, NULL);
+}
+
+void
 vt_suspend(struct vt_device *vd)
 {
        int error;

Modified: head/sys/sys/fbio.h
==============================================================================
--- head/sys/sys/fbio.h Sun Mar  1 12:47:36 2015        (r279487)
+++ head/sys/sys/fbio.h Sun Mar  1 12:54:22 2015        (r279488)
@@ -128,6 +128,9 @@ struct fb_info {
 
        struct cdev     *fb_cdev;
 
+       device_t         fb_fbd_dev;    /* "fbd" device. */
+       device_t         fb_video_dev;  /* Video adapter. */
+
        fb_enter_t      *enter;
        fb_leave_t      *leave;
        fb_setblankmode_t *setblankmode;
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to