From 78ded31d72180a41a2a67b8c104db18d4ca536b0 Mon Sep 17 00:00:00 2001
From: Sascha Wildner <saw@online.de>
Date: Tue, 26 May 2015 22:12:06 +0200
Subject: [PATCH] syscons kms 20150526

---
 sys/dev/drm/drm_fb_helper.c             |  30 +++
 sys/dev/drm/i915/Makefile               |   2 +
 sys/dev/drm/i915/intel_fbdev.c          | 377 ++++++++++++++++++++++++++++++++
 sys/dev/drm/include/drm/drm_fb_helper.h |   1 +
 sys/dev/drm/include/linux/fb.h          |  14 ++
 sys/dev/drm/radeon/radeon_fb.c          |  70 ++----
 sys/dev/misc/syscons/sckmsrndr.c        | 353 ++++++++++++++++++++++++++++++
 sys/dev/misc/syscons/scvidctl.c         | 125 ++++++++++-
 sys/dev/misc/syscons/syscons.c          |  78 ++++++-
 sys/dev/misc/syscons/syscons.h          |   7 +-
 sys/platform/pc64/conf/files            |   1 +
 11 files changed, 993 insertions(+), 65 deletions(-)
 create mode 100644 sys/dev/drm/i915/intel_fbdev.c
 create mode 100644 sys/dev/misc/syscons/sckmsrndr.c

diff --git a/sys/dev/drm/drm_fb_helper.c b/sys/dev/drm/drm_fb_helper.c
index 7b81cab..ea4c2ba 100644
--- a/sys/dev/drm/drm_fb_helper.c
+++ b/sys/dev/drm/drm_fb_helper.c
@@ -879,6 +879,31 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
 EXPORT_SYMBOL(drm_fb_helper_pan_display);
 #endif
 
+static void
+do_restore_fbdev_mode(void *context, int pending)
+{
+	struct drm_fb_helper *fb_helper = context;
+	struct drm_device *dev = fb_helper->dev;
+
+	if (!fb_helper->fb)
+		return;
+
+	drm_modeset_lock_all(dev);
+	drm_fb_helper_restore_fbdev_mode(fb_helper);
+	drm_modeset_unlock_all(dev);
+}
+
+static void
+sc_restore_fbdev_mode(void *cookie)
+{
+	struct drm_fb_helper *fb_helper = cookie;
+
+	if (!fb_helper->fb)
+		return;
+
+	taskqueue_enqueue(taskqueue_thread[0], &fb_helper->fb_mode_task);
+}
+
 /*
  * Allocates the backing storage and sets up the fbdev info structure through
  * the ->fb_probe callback and then registers the fbdev and sets up the panic
@@ -982,6 +1007,11 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
 		if (fb_helper->crtc_info[i].mode_set.num_connectors)
 			fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb;
 
+	TASK_INIT(&fb_helper->fb_mode_task, 0, do_restore_fbdev_mode, fb_helper);
+	info->cookie = fb_helper;
+	info->restore = (void *)&sc_restore_fbdev_mode;
+	if (register_framebuffer(info) < 0)
+		return -EINVAL;
 
 #if 0
 	info->var.pixclock = 0;
diff --git a/sys/dev/drm/i915/Makefile b/sys/dev/drm/i915/Makefile
index 29bb8a6..c4a2181 100644
--- a/sys/dev/drm/i915/Makefile
+++ b/sys/dev/drm/i915/Makefile
@@ -18,6 +18,7 @@ SRCS	= \
 	intel_ddi.c \
 	intel_display.c \
 	intel_dp.c \
+	intel_fbdev.c \
 	intel_hdmi.c \
 	intel_i2c.c \
 	intel_lvds.c \
@@ -39,6 +40,7 @@ SRCS	= \
 SRCS   += acpi_if.h device_if.h bus_if.h pci_if.h iicbus_if.h iicbb_if.h \
 	  opt_acpi.h opt_drm.h opt_ktr.h
 
+CFLAGS+= -DCONFIG_DRM_I915_FBDEV
 CFLAGS+= -I${SYSDIR}/dev/drm/include
 CFLAGS+= -I${.CURDIR}/../../../contrib/dev/acpica/source/include
 
diff --git a/sys/dev/drm/i915/intel_fbdev.c b/sys/dev/drm/i915/intel_fbdev.c
new file mode 100644
index 0000000..03b3b47
--- /dev/null
+++ b/sys/dev/drm/i915/intel_fbdev.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright © 2007 David Airlie
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *     David Airlie
+ */
+
+#include <drm/drmP.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_fb_helper.h>
+#include "intel_drv.h"
+#include <drm/i915_drm.h>
+#include "i915_drv.h"
+
+#if 0
+static struct fb_ops intelfb_ops = {
+	.owner = THIS_MODULE,
+	.fb_check_var = drm_fb_helper_check_var,
+	.fb_set_par = drm_fb_helper_set_par,
+	.fb_fillrect = cfb_fillrect,
+	.fb_copyarea = cfb_copyarea,
+	.fb_imageblit = cfb_imageblit,
+	.fb_pan_display = drm_fb_helper_pan_display,
+	.fb_blank = drm_fb_helper_blank,
+	.fb_setcmap = drm_fb_helper_setcmap,
+	.fb_debug_enter = drm_fb_helper_debug_enter,
+	.fb_debug_leave = drm_fb_helper_debug_leave,
+};
+#endif
+
+static int intelfb_alloc(struct drm_fb_helper *helper,
+			 struct drm_fb_helper_surface_size *sizes)
+{
+	struct intel_fbdev *ifbdev =
+		container_of(helper, struct intel_fbdev, helper);
+	struct drm_device *dev = helper->dev;
+	struct drm_mode_fb_cmd2 mode_cmd = {};
+	struct drm_i915_gem_object *obj;
+	int size, ret;
+
+	/* we don't do packed 24bpp */
+	if (sizes->surface_bpp == 24)
+		sizes->surface_bpp = 32;
+
+	mode_cmd.width = sizes->surface_width;
+	mode_cmd.height = sizes->surface_height;
+
+	mode_cmd.pitches[0] = ALIGN(mode_cmd.width *
+				    DIV_ROUND_UP(sizes->surface_bpp, 8), 64);
+	mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+							  sizes->surface_depth);
+
+	size = mode_cmd.pitches[0] * mode_cmd.height;
+	size = ALIGN(size, PAGE_SIZE);
+	obj = i915_gem_object_create_stolen(dev, size);
+	if (obj == NULL)
+		obj = i915_gem_alloc_object(dev, size);
+	if (!obj) {
+		DRM_ERROR("failed to allocate framebuffer\n");
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/* Flush everything out, we'll be doing GTT only from now on */
+	ret = intel_pin_and_fence_fb_obj(dev, obj, NULL);
+	if (ret) {
+		DRM_ERROR("failed to pin fb: %d\n", ret);
+		goto out_unref;
+	}
+
+	ret = intel_framebuffer_init(dev, &ifbdev->ifb, &mode_cmd, obj);
+	if (ret)
+		goto out_unpin;
+
+	return 0;
+
+out_unpin:
+	i915_gem_object_unpin(obj);
+out_unref:
+	drm_gem_object_unreference(&obj->base);
+out:
+	return ret;
+}
+
+static int intelfb_create(struct drm_fb_helper *helper,
+			  struct drm_fb_helper_surface_size *sizes)
+{
+	struct intel_fbdev *ifbdev =
+		container_of(helper, struct intel_fbdev, helper);
+	struct intel_framebuffer *intel_fb = &ifbdev->ifb;
+	struct drm_device *dev = helper->dev;
+#if 0
+	struct drm_i915_private *dev_priv = dev->dev_private;
+#endif
+	struct fb_info *info;
+	struct drm_framebuffer *fb;
+	struct drm_i915_gem_object *obj;
+	device_t vga_dev;
+	int size, ret;
+
+	mutex_lock(&dev->struct_mutex);
+
+	if (!intel_fb->obj) {
+		DRM_DEBUG_KMS("no BIOS fb, allocating a new one\n");
+		ret = intelfb_alloc(helper, sizes);
+		if (ret)
+			goto out_unlock;
+	} else {
+		DRM_DEBUG_KMS("re-using BIOS fb\n");
+		sizes->fb_width = intel_fb->base.width;
+		sizes->fb_height = intel_fb->base.height;
+	}
+
+	obj = intel_fb->obj;
+	size = obj->base.size;
+
+#if 0
+	info = framebuffer_alloc(0, &dev->pdev->dev);
+	if (!info) {
+		ret = -ENOMEM;
+		goto out_unpin;
+	}
+
+	info->par = helper;
+#endif
+	vga_dev = device_get_parent(dev->dev);
+	info = kmalloc(sizeof(struct fb_info), M_DRM, M_WAITOK | M_ZERO);
+	info->width = sizes->fb_width;
+	info->height = sizes->fb_height;
+	info->stride = ALIGN(sizes->surface_width * ((sizes->surface_bpp + 7) / 8), 64);
+	info->depth = sizes->surface_bpp;
+	info->paddr = dev->agp->base + i915_gem_obj_ggtt_offset(obj);
+	info->is_vga_boot_display = vga_pci_is_boot_display(vga_dev);
+	info->vaddr =
+	    (vm_offset_t)pmap_mapdev_attr(info->paddr,
+		sizes->surface_height * info->stride,
+		VM_MEMATTR_WRITE_COMBINING);
+
+	fb = &ifbdev->ifb.base;
+
+	ifbdev->helper.fb = fb;
+	ifbdev->helper.fbdev = info;
+
+#if 0
+	strcpy(info->fix.id, "inteldrmfb");
+
+	info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
+	info->fbops = &intelfb_ops;
+
+	ret = fb_alloc_cmap(&info->cmap, 256, 0);
+	if (ret) {
+		ret = -ENOMEM;
+		goto out_unpin;
+	}
+	/* setup aperture base/size for vesafb takeover */
+	info->apertures = alloc_apertures(1);
+	if (!info->apertures) {
+		ret = -ENOMEM;
+		goto out_unpin;
+	}
+	info->apertures->ranges[0].base = dev->mode_config.fb_base;
+	info->apertures->ranges[0].size = dev_priv->gtt.mappable_end;
+
+	info->fix.smem_start = dev->mode_config.fb_base + i915_gem_obj_ggtt_offset(obj);
+	info->fix.smem_len = size;
+
+	info->screen_base =
+		ioremap_wc(dev_priv->gtt.mappable_base + i915_gem_obj_ggtt_offset(obj),
+			   size);
+	if (!info->screen_base) {
+		ret = -ENOSPC;
+		goto out_unpin;
+	}
+	info->screen_size = size;
+
+	/* This driver doesn't need a VT switch to restore the mode on resume */
+	info->skip_vt_switch = true;
+
+	drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
+	drm_fb_helper_fill_var(info, &ifbdev->helper, sizes->fb_width, sizes->fb_height);
+
+	/* If the object is shmemfs backed, it will have given us zeroed pages.
+	 * If the object is stolen however, it will be full of whatever
+	 * garbage was left in there.
+	 */
+	if (ifbdev->ifb.obj->stolen)
+		memset_io(info->screen_base, 0, info->screen_size);
+
+	/* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
+#endif
+
+	DRM_DEBUG_KMS("allocated %dx%d fb: 0x%08lx, bo %p\n",
+		      fb->width, fb->height,
+		      i915_gem_obj_ggtt_offset(obj), obj);
+
+	mutex_unlock(&dev->struct_mutex);
+#if 0
+	vga_switcheroo_client_fb_set(dev->pdev, info);
+#endif
+	return 0;
+
+#if 0
+out_unpin:
+	i915_gem_object_unpin(obj);
+	drm_gem_object_unreference(&obj->base);
+#endif
+out_unlock:
+	mutex_unlock(&dev->struct_mutex);
+	return ret;
+}
+
+/** Sets the color ramps on behalf of RandR */
+static void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
+				    u16 blue, int regno)
+{
+	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+	intel_crtc->lut_r[regno] = red >> 8;
+	intel_crtc->lut_g[regno] = green >> 8;
+	intel_crtc->lut_b[regno] = blue >> 8;
+}
+
+static void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
+				    u16 *blue, int regno)
+{
+	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+	*red = intel_crtc->lut_r[regno] << 8;
+	*green = intel_crtc->lut_g[regno] << 8;
+	*blue = intel_crtc->lut_b[regno] << 8;
+}
+
+static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
+	.gamma_set = intel_crtc_fb_gamma_set,
+	.gamma_get = intel_crtc_fb_gamma_get,
+	.fb_probe = intelfb_create,
+};
+
+static void intel_fbdev_destroy(struct drm_device *dev,
+				struct intel_fbdev *ifbdev)
+{
+#if 0
+	if (ifbdev->helper.fbdev) {
+		struct fb_info *info = ifbdev->helper.fbdev;
+
+		unregister_framebuffer(info);
+		iounmap(info->screen_base);
+		if (info->cmap.len)
+			fb_dealloc_cmap(&info->cmap);
+
+		framebuffer_release(info);
+	}
+#endif
+
+	drm_fb_helper_fini(&ifbdev->helper);
+
+	drm_framebuffer_unregister_private(&ifbdev->ifb.base);
+	intel_framebuffer_fini(&ifbdev->ifb);
+}
+
+int intel_fbdev_init(struct drm_device *dev)
+{
+	struct intel_fbdev *ifbdev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	int ret;
+
+	ifbdev = kzalloc(sizeof(*ifbdev), GFP_KERNEL);
+	if (!ifbdev)
+		return -ENOMEM;
+
+	dev_priv->fbdev = ifbdev;
+	ifbdev->helper.funcs = &intel_fb_helper_funcs;
+
+	ret = drm_fb_helper_init(dev, &ifbdev->helper,
+				 INTEL_INFO(dev)->num_pipes,
+				 4);
+	if (ret) {
+		kfree(ifbdev);
+		return ret;
+	}
+
+	drm_fb_helper_single_add_all_connectors(&ifbdev->helper);
+
+	return 0;
+}
+
+void intel_fbdev_initial_config(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+
+	/* Due to peculiar init order wrt to hpd handling this is separate. */
+	drm_fb_helper_initial_config(&dev_priv->fbdev->helper, 32);
+}
+
+void intel_fbdev_fini(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	if (!dev_priv->fbdev)
+		return;
+
+	intel_fbdev_destroy(dev, dev_priv->fbdev);
+	kfree(dev_priv->fbdev);
+	dev_priv->fbdev = NULL;
+}
+
+void intel_fbdev_set_suspend(struct drm_device *dev, int state)
+{
+#if 0
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_fbdev *ifbdev = dev_priv->fbdev;
+	struct fb_info *info;
+
+	if (!ifbdev)
+		return;
+
+	info = ifbdev->helper.fbdev;
+
+	/* On resume from hibernation: If the object is shmemfs backed, it has
+	 * been restored from swap. If the object is stolen however, it will be
+	 * full of whatever garbage was left in there.
+	 */
+	if (state == FBINFO_STATE_RUNNING && ifbdev->ifb.obj->stolen)
+		memset_io(info->screen_base, 0, info->screen_size);
+
+	fb_set_suspend(info, state);
+#endif
+}
+
+void intel_fbdev_output_poll_changed(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	drm_fb_helper_hotplug_event(&dev_priv->fbdev->helper);
+}
+
+void intel_fbdev_restore_mode(struct drm_device *dev)
+{
+	int ret;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+
+	if (INTEL_INFO(dev)->num_pipes == 0)
+		return;
+
+	drm_modeset_lock_all(dev);
+
+	ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper);
+	if (ret)
+		DRM_DEBUG("failed to restore crtc mode\n");
+
+	drm_modeset_unlock_all(dev);
+}
diff --git a/sys/dev/drm/include/drm/drm_fb_helper.h b/sys/dev/drm/include/drm/drm_fb_helper.h
index 08f7950..ae598ac 100644
--- a/sys/dev/drm/include/drm/drm_fb_helper.h
+++ b/sys/dev/drm/include/drm/drm_fb_helper.h
@@ -86,6 +86,7 @@ struct drm_fb_helper {
 	int connector_count;
 	struct drm_fb_helper_connector **connector_info;
 	struct drm_fb_helper_funcs *funcs;
+	struct task fb_mode_task;
 	struct fb_info *fbdev;
 	u32 pseudo_palette[17];
 	struct list_head kernel_fb_list;
diff --git a/sys/dev/drm/include/linux/fb.h b/sys/dev/drm/include/linux/fb.h
index 22e2cb7..09ac33c 100644
--- a/sys/dev/drm/include/linux/fb.h
+++ b/sys/dev/drm/include/linux/fb.h
@@ -33,4 +33,18 @@
 
 #define	KHZ2PICOS(a)	(1000000000UL/(a))
 
+struct fb_info {
+	vm_offset_t vaddr;
+	vm_paddr_t paddr;
+	uint16_t width;
+	uint16_t height;
+	uint16_t stride;
+	uint16_t depth;
+	int is_vga_boot_display;
+	void *cookie;
+	void (*restore)(void *);
+};
+
+extern int register_framebuffer(struct fb_info *fb_info);
+
 #endif	/* _LINUX_FB_H_ */
diff --git a/sys/dev/drm/radeon/radeon_fb.c b/sys/dev/drm/radeon/radeon_fb.c
index 012589b..caf0e3a 100644
--- a/sys/dev/drm/radeon/radeon_fb.c
+++ b/sys/dev/drm/radeon/radeon_fb.c
@@ -191,9 +191,7 @@ static int radeonfb_create(struct drm_fb_helper *helper,
 {
 	struct radeon_fbdev *rfbdev = (struct radeon_fbdev *)helper;
 	struct radeon_device *rdev = rfbdev->rdev;
-#ifdef DUMBBELL_WIP
 	struct fb_info *info;
-#endif /* DUMBBELL_WIP */
 	struct drm_framebuffer *fb = NULL;
 	struct drm_mode_fb_cmd2 mode_cmd;
 	struct drm_gem_object *gobj = NULL;
@@ -201,10 +199,9 @@ static int radeonfb_create(struct drm_fb_helper *helper,
 #ifdef DUMBBELL_WIP
 	device_t device = rdev->dev;
 #endif /* DUMBBELL_WIP */
+	device_t vga_dev = device_get_parent(rdev->dev);
 	int ret;
-#ifdef DUMBBELL_WIP
 	unsigned long tmp;
-#endif /* DUMBBELL_WIP */
 
 	mode_cmd.width = sizes->surface_width;
 	mode_cmd.height = sizes->surface_height;
@@ -224,16 +221,7 @@ static int radeonfb_create(struct drm_fb_helper *helper,
 
 	rbo = gem_to_radeon_bo(gobj);
 
-#ifdef DUMBBELL_WIP
-	/* okay we have an object now allocate the framebuffer */
-	info = framebuffer_alloc(0, device);
-	if (info == NULL) {
-		ret = -ENOMEM;
-		goto out_unref;
-	}
-
-	info->par = rfbdev;
-#endif /* DUMBBELL_WIP */
+	info = kmalloc(sizeof(*info), M_DRM, M_WAITOK | M_ZERO);
 
 	ret = radeon_framebuffer_init(rdev->ddev, &rfbdev->rfb, &mode_cmd, gobj);
 	if (ret) {
@@ -245,56 +233,23 @@ static int radeonfb_create(struct drm_fb_helper *helper,
 
 	/* setup helper */
 	rfbdev->helper.fb = fb;
-#ifdef DUMBBELL_WIP
 	rfbdev->helper.fbdev = info;
 
-	memset_io(rbo->kptr, 0x0, radeon_bo_size(rbo));
-
-	strcpy(info->fix.id, "radeondrmfb");
-
-	drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
-
-	info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
-	info->fbops = &radeonfb_ops;
-
+	memset(rbo->kptr, 0, radeon_bo_size(rbo));
 	tmp = radeon_bo_gpu_offset(rbo) - rdev->mc.vram_start;
-	info->fix.smem_start = rdev->mc.aper_base + tmp;
-	info->fix.smem_len = radeon_bo_size(rbo);
-	info->screen_base = rbo->kptr;
-	info->screen_size = radeon_bo_size(rbo);
-
-	drm_fb_helper_fill_var(info, &rfbdev->helper, sizes->fb_width, sizes->fb_height);
-
-	/* setup aperture base/size for vesafb takeover */
-	info->apertures = alloc_apertures(1);
-	if (!info->apertures) {
-		ret = -ENOMEM;
-		goto out_unref;
-	}
-	info->apertures->ranges[0].base = rdev->ddev->mode_config.fb_base;
-	info->apertures->ranges[0].size = rdev->mc.aper_size;
-
-	/* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
-
-	if (info->screen_base == NULL) {
-		ret = -ENOSPC;
-		goto out_unref;
-	}
-
-	ret = fb_alloc_cmap(&info->cmap, 256, 0);
-	if (ret) {
-		ret = -ENOMEM;
-		goto out_unref;
-	}
-
-	DRM_INFO("fb mappable at 0x%lX\n",  info->fix.smem_start);
+	info->vaddr = (vm_offset_t)rbo->kptr;
+	info->paddr = rdev->mc.aper_base + tmp;
+	info->width = sizes->surface_width;
+	info->height = sizes->surface_height;
+	info->stride = sizes->surface_width * (sizes->surface_bpp/8);
+	info->depth = sizes->surface_bpp;
+	info->is_vga_boot_display = vga_pci_is_boot_display(vga_dev);
+
+	DRM_INFO("fb mappable at 0x%jX\n",  info->paddr);
 	DRM_INFO("vram apper at 0x%lX\n",  (unsigned long)rdev->mc.aper_base);
 	DRM_INFO("size %lu\n", (unsigned long)radeon_bo_size(rbo));
 	DRM_INFO("fb depth is %d\n", fb->depth);
 	DRM_INFO("   pitch is %d\n", fb->pitches[0]);
-
-	vga_switcheroo_client_fb_set(rdev->ddev->pdev, info);
-#endif /* DUMBBELL_WIP */
 	return 0;
 
 out_unref:
@@ -317,6 +272,7 @@ void radeon_fb_output_poll_changed(struct radeon_device *rdev)
 
 static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfbdev)
 {
+	/* XXX unconfigure fb_info from syscons */
 #ifdef DUMBBELL_WIP
 	struct fb_info *info;
 #endif /* DUMBBELL_WIP */
diff --git a/sys/dev/misc/syscons/sckmsrndr.c b/sys/dev/misc/syscons/sckmsrndr.c
new file mode 100644
index 0000000..2ceb526
--- /dev/null
+++ b/sys/dev/misc/syscons/sckmsrndr.c
@@ -0,0 +1,353 @@
+/*-
+ * Copyright (c) 2014 Imre Vadász <imre@vdsz.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer as
+ *    the first lines of this file unmodified.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "opt_syscons.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/thread.h>
+#include <sys/thread2.h>
+
+#include <machine/console.h>
+
+#include <dev/drm/include/linux/fb.h>
+
+#include "syscons.h"
+
+#include <bus/isa/isareg.h>
+
+static vr_draw_t		kms_draw;
+static vr_draw_cursor_t		kms_cursor;
+static vr_blink_cursor_t	kms_blink;
+static vr_draw_mouse_t		kms_mouse;
+
+static void			kms_nop(scr_stat *scp, ...);
+
+static sc_rndr_sw_t kmsrndrsw = {
+	(vr_draw_border_t *)kms_nop,
+	kms_draw,
+	(vr_set_cursor_t *)kms_nop,
+	kms_cursor,
+	kms_blink,
+#ifndef SC_NO_CUTPASTE
+	kms_mouse,
+#else
+	(vr_draw_mouse_t *)kms_nop;
+#endif
+};
+RENDERER(kms, V_INFO_MM_TEXT, kmsrndrsw, kms_set);
+
+#ifndef SC_NO_MODE_CHANGE
+static sc_rndr_sw_t grrndrsw = {
+	(vr_draw_border_t *)kms_nop,
+	(vr_draw_t *)kms_nop,
+	(vr_set_cursor_t *)kms_nop,
+	(vr_draw_cursor_t *)kms_nop,
+	(vr_blink_cursor_t *)kms_nop,
+	(vr_draw_mouse_t *)kms_nop,
+};
+RENDERER(kms, V_INFO_MM_OTHER, grrndrsw, kms_set);
+#endif /* SC_NO_MODE_CHANGE */
+
+RENDERER_MODULE(kms, kms_set);
+
+static uint32_t colormap[16] = {
+	0x00000000,	/* BLACK */
+	0x000000aa,	/* BLUE */
+	0x0000aa00,	/* GREEN */
+	0x0000aaaa,	/* CYAN */
+	0x00aa0000,	/* RED */
+	0x00aa00aa,	/* MAGENTA */
+	0x00aa5500,	/* BROWN */
+	0x00aaaaaa,	/* WHITE */
+	0x00555555,	/* HIGHLIGHT BLACK */
+	0x005555ff,	/* HIGHLIGHT BLUE */
+	0x0055ff55,	/* HIGHLIGHT GREEN */
+	0x0055ffff,	/* HIGHLIGHT CYAN */
+	0x00ff5555,	/* HIGHLIGHT RED */
+	0x00ff55ff,	/* HIGHLIGHT MAGENTA */
+	0x00ffff55,	/* HIGHLIGHT BROWN */
+	0x00ffffff,	/* HIGHLIGHT WHITE */
+};
+
+#ifndef SC_NO_CUTPASTE
+static u_short mouse_and_mask[16] = {
+	0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00, 0xff80,
+	0xfe00, 0x1e00, 0x1f00, 0x0f00, 0x0f00, 0x0000, 0x0000, 0x0000
+};
+static u_short mouse_or_mask[16] = {
+	0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7c00, 0x7e00, 0x6800,
+	0x0c00, 0x0c00, 0x0600, 0x0600, 0x0000, 0x0000, 0x0000, 0x0000
+};
+#endif
+
+static void
+kms_nop(scr_stat *scp, ...)
+{
+}
+
+/* KMS renderer */
+
+static void
+kms_draw(scr_stat *scp, int from, int count, int flip)
+{
+	sc_softc_t *sc = scp->sc;
+	u_char *char_data;
+	int a, i, j;
+	uint32_t fg, bg;
+	vm_offset_t draw_pos, p;
+	int pos, line_width, pixel_size;
+
+	line_width = sc->fbi->stride;
+	pixel_size = 4;
+
+	draw_pos = sc->fbi->vaddr +
+#if 0 /* XXX fix xoff and yoff */
+	    8 * scp->font_mag * pixel_size * scp->xoff +
+	    scp->yoff * scp->font_size * scp->font_mag * line_width +
+#endif
+	    8 * scp->font_mag * pixel_size * (from % scp->xsize) +
+	    scp->font_mag * scp->font_size * line_width * (from / scp->xsize);
+
+	if (from + count > scp->xsize * scp->ysize)
+		count = scp->xsize * scp->ysize - from;
+
+	for (i = from; count-- > 0; i++) {
+		p = draw_pos;
+		char_data = &(scp->font[sc_vtb_getc(&scp->vtb, i) *
+		    scp->font_size]);
+
+		a = sc_vtb_geta(&scp->vtb, i);
+		if (flip) {
+			fg = colormap[((a & 0xf000) >> 4) >> 8];
+			bg = colormap[(a & 0x0f00) >> 8];
+		} else {
+			fg = colormap[(a & 0x0f00) >> 8];
+			bg = colormap[((a & 0xf000) >> 4) >> 8];
+		}
+
+		for (j = 0; j < scp->font_size; j++, char_data++) {
+			for (pos = 7; pos >= 0; pos--, p += pixel_size * scp->font_mag) {
+				for (int xp = 0; xp < scp->font_mag; xp++) {
+					for (int yp = 0; yp < scp->font_mag; yp++) {
+						writel(p +
+						    xp * pixel_size +
+						    yp * line_width,
+						    *char_data & (1 << pos) ?
+						    fg : bg);
+					}
+				}
+			}
+
+			p += scp->font_mag * line_width -
+			    8 * scp->font_mag * pixel_size;
+		}
+		draw_pos += 8 * scp->font_mag * pixel_size;
+		if ((i % scp->xsize) == scp->xsize - 1) {
+			draw_pos +=
+#if 0 /* XXX fix xoff and yoff */
+			    scp->xoff * 16 * scp->font_mag * pixel_size +
+#endif
+			    (scp->font_size * scp->font_mag - 1) * line_width +
+			    scp->xpad * pixel_size;
+		}
+	}
+}
+
+static void
+draw_kmscursor(scr_stat *scp, int at, int on, int flip)
+{
+	sc_softc_t *sc = scp->sc;
+	int line_width, pixel_size, height;
+	int a, i, pos;
+	uint32_t fg, bg;
+	unsigned char *char_data;
+	vm_offset_t draw_pos;
+
+	line_width = sc->fbi->stride;
+	pixel_size = 4;
+
+	draw_pos = sc->fbi->vaddr +
+#if 0 /* XXX fix xoff and yoff */
+	    8 * pixel_size * scp->xoff +
+	    scp->yoff * scp->font_size * line_width +
+#endif
+	    8 * scp->font_mag * pixel_size * (at % scp->xsize) +
+	    scp->font_mag * scp->font_size * line_width * (at / scp->xsize) +
+	    (scp->font_size - scp->cursor_base - 1) * scp->font_mag * line_width;
+
+	a = sc_vtb_geta(&scp->vtb, at);
+	if (flip) {
+		fg = colormap[((on) ? (a & 0x0f00) :
+		    ((a & 0xf000) >> 4)) >> 8];
+		bg = colormap[((on) ? ((a & 0xf000) >> 4) :
+		    (a & 0x0f00)) >> 8];
+	} else {
+		fg = colormap[((on) ? ((a & 0xf000) >> 4) :
+		    (a & 0x0f00)) >> 8];
+		bg = colormap[((on) ? (a & 0x0f00) :
+		    ((a & 0xf000) >> 4)) >> 8];
+	}
+
+	char_data = &(scp->font[sc_vtb_getc(&scp->vtb, at) * scp->font_size +
+	    scp->font_size - scp->cursor_base - 1]);
+	height = imin(scp->cursor_height, scp->font_size);
+
+	for (i = 0; i < height; i++, char_data--) {
+		for (pos = 7; pos >= 0; pos--, draw_pos += pixel_size * scp->font_mag) {
+			for (int xp = 0; xp < scp->font_mag; xp++) {
+				for (int yp = 0; yp < scp->font_mag; yp++) {
+					writel(draw_pos +
+					    xp * pixel_size +
+					    yp * line_width,
+					    *char_data & (1 << pos) ?
+					    fg : bg);
+				}
+			}
+		}
+		draw_pos -= scp->font_mag * line_width +
+		    8 * scp->font_mag * pixel_size;
+	}
+}
+
+static int pxlblinkrate = 0;
+
+static void
+kms_cursor(scr_stat *scp, int at, int blink, int on, int flip)
+{
+	if (scp->cursor_height <= 0)	/* the text cursor is disabled */
+		return;
+
+	if (on) {
+		if (!blink) {
+			scp->status |= VR_CURSOR_ON;
+			draw_kmscursor(scp, at, on, flip);
+		} else if (++pxlblinkrate & 4) {
+			pxlblinkrate = 0;
+			scp->status ^= VR_CURSOR_ON;
+			draw_kmscursor(scp, at,
+			    scp->status & VR_CURSOR_ON, flip);
+		}
+	} else {
+		if (scp->status & VR_CURSOR_ON)
+			draw_kmscursor(scp, at, on, flip);
+		scp->status &= ~VR_CURSOR_ON;
+	}
+	if (blink)
+		scp->status |= VR_CURSOR_BLINK;
+	else
+		scp->status &= ~VR_CURSOR_BLINK;
+}
+
+static void
+kms_blink(scr_stat *scp, int at, int flip)
+{
+	if (!(scp->status & VR_CURSOR_BLINK))
+		return;
+	if (!(++pxlblinkrate & 4))
+		return;
+	pxlblinkrate = 0;
+	scp->status ^= VR_CURSOR_ON;
+	draw_kmscursor(scp, at, scp->status & VR_CURSOR_ON, flip);
+}
+
+#ifndef SC_NO_CUTPASTE
+
+static void
+draw_kmsmouse(scr_stat *scp, int x, int y)
+{
+	sc_softc_t *sc = scp->sc;
+	int line_width, pixel_size;
+	int xend, yend;
+	int i, j;
+	vm_offset_t draw_pos;
+
+	line_width = sc->fbi->stride;
+	pixel_size = 4;
+
+	xend = imin(x + 8, 8 * (scp->xoff + scp->xsize));
+	yend = imin(y + 16, scp->font_size * (scp->yoff + scp->ysize));
+
+	draw_pos = sc->fbi->vaddr + y * scp->font_mag * line_width +
+	    x * scp->font_mag * pixel_size;
+
+	for (i = 0; i < (yend - y); i++) {
+		for (j = (xend - x - 1); j >= 0; j--) {
+			if (mouse_or_mask[i] & 1 << (15 - j)) {
+				for (int xp = 0; xp < scp->font_mag; xp++) {
+					for (int yp = 0; yp < scp->font_mag; yp++) {
+						writel(draw_pos +
+						    scp->font_mag * pixel_size * j +
+						    xp * pixel_size +
+						    yp * line_width,
+						    colormap[15]);
+					}
+				}
+			} else if (mouse_and_mask[i] & 1 << (15 - j)) {
+				for (int xp = 0; xp < scp->font_mag; xp++) {
+					for (int yp = 0; yp < scp->font_mag; yp++) {
+						writel(draw_pos +
+						    scp->font_mag * pixel_size * j +
+						    xp * pixel_size +
+						    yp * line_width,
+						    colormap[0]);
+					}
+				}
+			}
+		}
+
+		draw_pos += scp->font_mag * line_width;
+	}
+}
+
+static void
+remove_kmsmouse(scr_stat *scp, int x, int y)
+{
+	int col, row;
+	int pos;
+	int i;
+
+	/* erase the mouse cursor image */
+	col = x/8 - scp->xoff;
+	row = y/scp->font_size - scp->yoff;
+	pos = row*scp->xsize + col;
+	i = (col < scp->xsize - 1) ? 2 : 1;
+	(*scp->rndr->draw)(scp, pos, i, FALSE);
+	if (row < scp->ysize - 1)
+		(*scp->rndr->draw)(scp, pos + scp->xsize, i, FALSE);
+}
+
+static void
+kms_mouse(scr_stat *scp, int x, int y, int on)
+{
+	if (on)
+		draw_kmsmouse(scp, x, y);
+	else
+		remove_kmsmouse(scp, x, y);
+}
+
+#endif /* SC_NO_CUTPASTE */
diff --git a/sys/dev/misc/syscons/scvidctl.c b/sys/dev/misc/syscons/scvidctl.c
index a8cbe87..22fe1e4 100644
--- a/sys/dev/misc/syscons/scvidctl.c
+++ b/sys/dev/misc/syscons/scvidctl.c
@@ -29,7 +29,6 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/dev/syscons/scvidctl.c,v 1.19.2.2 2000/05/05 09:16:08 nyan Exp $
- * $DragonFly: src/sys/dev/misc/syscons/scvidctl.c,v 1.16 2007/08/19 11:39:11 swildner Exp $
  */
 
 #include "opt_syscons.h"
@@ -44,6 +43,7 @@
 
 #include <machine/console.h>
 
+#include <dev/drm/include/linux/fb.h>
 #include <dev/video/fb/fbreg.h>
 #include "syscons.h"
 
@@ -109,7 +109,13 @@ sc_set_text_mode(scr_stat *scp, struct tty *tp, int mode, int xsize, int ysize,
 	return error;
     }
 
-    if (sc_render_match(scp, scp->sc->adp->va_name, V_INFO_MM_TEXT) == NULL) {
+    if (scp->sc->fbi != NULL &&
+	sc_render_match(scp, "kms", V_INFO_MM_TEXT) == NULL) {
+	crit_exit();
+	return ENODEV;
+    }
+    if (scp->sc->fbi == NULL &&
+	sc_render_match(scp, scp->sc->adp->va_name, V_INFO_MM_TEXT) == NULL) {
 	crit_exit();
 	return ENODEV;
     }
@@ -192,7 +198,13 @@ sc_set_graphics_mode(scr_stat *scp, struct tty *tp, int mode)
 	return error;
     }
 
-    if (sc_render_match(scp, scp->sc->adp->va_name, V_INFO_MM_OTHER) == NULL) {
+    if (scp->sc->fbi != NULL &&
+	sc_render_match(scp, "kms", V_INFO_MM_OTHER) == NULL) {
+	crit_exit();
+	return ENODEV;
+    }
+    if (scp->sc->fbi == NULL &&
+	sc_render_match(scp, scp->sc->adp->va_name, V_INFO_MM_OTHER) == NULL) {
 	crit_exit();
 	return ENODEV;
     }
@@ -405,7 +417,9 @@ sc_vid_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag)
 {
     scr_stat *scp;
     video_adapter_t *adp;
+#ifndef SC_NO_MODE_CHANGE
     video_info_t info;
+#endif
     int error, ret;
 
 	KKASSERT(tp->t_dev);
@@ -729,3 +743,108 @@ sc_render_match(scr_stat *scp, char *name, int model)
 
 	return NULL;
 }
+
+#define VIRTUAL_TTY(sc, x) ((SC_DEV((sc),(x)) != NULL) ?	\
+	(SC_DEV((sc),(x))->si_tty) : NULL)
+
+void
+sc_update_render(scr_stat *scp)
+{
+	sc_rndr_sw_t *rndr;
+	sc_term_sw_t *sw;
+	struct tty *tp;
+	int prev_ysize, new_ysize;
+	int error;
+
+	sw = scp->tsw;
+	if (sw == NULL) {
+		return;
+	}
+
+	if (scp->rndr == NULL)
+		return;
+
+	if (scp->fbi == scp->sc->fbi)
+		return;
+
+	crit_enter();
+	scp->fbi = scp->sc->fbi;
+	rndr = NULL;
+	if (strcmp(sw->te_renderer, "*") != 0) {
+		rndr = sc_render_match(scp, sw->te_renderer, scp->model);
+	}
+	if (rndr == NULL && scp->sc->fbi != NULL) {
+		rndr = sc_render_match(scp, "kms", scp->model);
+	}
+	if (rndr != NULL) {
+		scp->rndr = rndr;
+		/* Mostly copied from sc_set_text_mode */
+		if ((error = sc_clean_up(scp))) {
+			crit_exit();
+			return;
+		}
+		new_ysize = 0;
+#ifndef SC_NO_HISTORY
+		if (scp->history != NULL) {
+			sc_hist_save(scp);
+			new_ysize = sc_vtb_rows(scp->history);
+		}
+#endif
+		prev_ysize = scp->ysize;
+		scp->status |= UNKNOWN_MODE | MOUSE_HIDDEN;
+		scp->status &= ~(GRAPHICS_MODE | PIXEL_MODE | MOUSE_VISIBLE);
+		scp->model = V_INFO_MM_TEXT;
+		scp->font_mag = 1;
+		scp->xpixel = scp->fbi->width;
+		scp->ypixel = scp->fbi->height;
+		scp->xsize = scp->xpixel / 8;
+		scp->ysize = scp->ypixel / scp->font_size;
+		scp->xpad = scp->fbi->stride / 4 - scp->xsize * 8;
+		while (scp->xsize / (scp->font_mag + 1) >= 80 &&
+		    scp->ysize / (scp->font_mag + 1) >= 24)
+			scp->font_mag++;
+		scp->xsize /= scp->font_mag;
+		scp->ysize /= scp->font_mag;
+#if 0 /* test */
+		scp->xsize = 35;
+		scp->ysize = 10;
+		scp->font_mag = 6;
+#endif
+#if 0 /* XXX fix xoff and yoff */
+		scp->xoff =
+		    (scp->xpixel - (scp->xsize * 8 * scp->font_mag)) / 2;
+		scp->yoff =
+		    (scp->ypixel - (scp->ysize * scp->font_size * scp->font_mag)) / 2;
+#endif
+
+		/* allocate buffers */
+		sc_alloc_scr_buffer(scp, TRUE, TRUE);
+		sc_init_emulator(scp, NULL);
+#ifndef SC_NO_CUTPASTE
+		sc_alloc_cut_buffer(scp, FALSE);
+#endif
+#ifndef SC_NO_HISTORY
+		sc_alloc_history_buffer(scp, new_ysize, prev_ysize, FALSE);
+#endif
+		crit_exit();
+		scp->status &= ~UNKNOWN_MODE;
+		tp = VIRTUAL_TTY(scp->sc, scp->index);
+		if (tp == NULL)
+			return;
+		if (tp->t_winsize.ws_col != scp->xsize ||
+		    tp->t_winsize.ws_row != scp->ysize) {
+			tp->t_winsize.ws_col = scp->xsize;
+			tp->t_winsize.ws_row = scp->ysize;
+			pgsignal(tp->t_pgrp, SIGWINCH, 1);
+		}
+		return;
+	}
+	if (rndr == NULL) {
+		rndr = sc_render_match(scp, scp->sc->adp->va_name, scp->model);
+	}
+
+	if (rndr != NULL) {
+		scp->rndr = rndr;
+	}
+	crit_exit();
+}
diff --git a/sys/dev/misc/syscons/syscons.c b/sys/dev/misc/syscons/syscons.c
index feba21e..83e4d5a 100644
--- a/sys/dev/misc/syscons/syscons.c
+++ b/sys/dev/misc/syscons/syscons.c
@@ -64,6 +64,7 @@
 #endif
 #include <machine/frame.h>
 
+#include <dev/drm/include/linux/fb.h>
 #include <dev/misc/kbd/kbdreg.h>
 #include <dev/video/fb/fbreg.h>
 #include <dev/video/fb/splashreg.h>
@@ -273,6 +274,41 @@ sc_probe_unit(int unit, int flags)
     return 0;
 }
 
+int
+register_framebuffer(struct fb_info *info)
+{
+    sc_softc_t *sc;
+
+    /* For now ignore framebuffers, which don't replace the vga display */
+    if (!info->is_vga_boot_display)
+	return 0;
+
+    lwkt_gettoken(&tty_token);
+    sc = sc_get_softc(0, (sc_console_unit == 0) ? SC_KERNEL_CONSOLE : 0);
+    if (sc == NULL) {
+	lwkt_reltoken(&tty_token);
+        kprintf("%s: sc_get_softc(%d, %d) returned NULL\n", __func__,
+	    0, (sc_console_unit == 0) ? SC_KERNEL_CONSOLE : 0);
+	return 0;
+    }
+
+    /* Ignore this framebuffer if we have already switched to a framebuffer */
+    if (sc->fbi != NULL) {
+	lwkt_reltoken(&tty_token);
+	return 0;
+    }
+
+    sc->fbi = info;
+
+    if (sc->fbi != NULL) {
+	sc_update_render(sc->cur_scp);
+	sc->fbi->restore(sc->fbi->cookie);
+    }
+
+    lwkt_reltoken(&tty_token);
+    return 0;
+}
+
 /* probe video adapters, return TRUE if found */ 
 static int
 scvidprobe(int unit, int flags, int cons)
@@ -2636,6 +2672,7 @@ exchange_scr(sc_softc_t *sc)
 	sc_vtb_init(&scp->scr, VTB_FRAMEBUFFER, scp->xsize, scp->ysize,
 		    (void *)sc->adp->va_window, FALSE);
     scp->status |= MOUSE_HIDDEN;
+    sc_update_render(scp);     /* Switch to kms renderer if necessary */
     sc_move_cursor(scp, scp->xpos, scp->ypos);
     if (!ISGRAPHSC(scp))
 	sc_set_cursor_image(scp);
@@ -2651,6 +2688,9 @@ exchange_scr(sc_softc_t *sc)
     update_kbd_state(scp, scp->status, LOCK_MASK, TRUE);
 
     mark_all(scp);
+    if (scp->sc->fbi != NULL) {
+	scp->sc->fbi->restore(scp->sc->fbi->cookie);
+    }
     lwkt_reltoken(&tty_token);
 }
 
@@ -3149,10 +3189,33 @@ init_scp(sc_softc_t *sc, int vty, scr_stat *scp)
 #endif
 	}
     }
-    sc_vtb_init(&scp->vtb, VTB_MEMORY, 0, 0, NULL, FALSE);
-    sc_vtb_init(&scp->scr, VTB_FRAMEBUFFER, 0, 0, NULL, FALSE);
     scp->xoff = scp->yoff = 0;
     scp->xpos = scp->ypos = 0;
+    scp->font_mag = 1;
+    scp->fbi = sc->fbi;
+    if (scp->fbi != NULL) {
+	scp->xpixel = scp->fbi->width;
+	scp->ypixel = scp->fbi->height;
+	scp->xsize = scp->xpixel / 8;
+	scp->ysize = scp->ypixel / scp->font_size;
+	while (scp->xsize / (scp->font_mag + 1) >= 80 &&
+	    scp->ysize / (scp->font_mag + 1) >= 25)
+		scp->font_mag++;
+	scp->xsize /= scp->font_mag;
+	scp->ysize /= scp->font_mag;
+#if 0 /* test */
+	scp->xsize = 35;
+	scp->ysize = 10;
+	scp->font_mag = 6;
+#endif
+#if 0 /* XXX fix xoff and yoff */
+	scp->xoff = (scp->xpixel - (scp->xsize * 8 * scp->font_mag)) / 2;
+	scp->yoff =
+	    (scp->ypixel - (scp->xsize * scp->font_size * scp->font_mag)) / 2;
+#endif
+    }
+    sc_vtb_init(&scp->vtb, VTB_MEMORY, 0, 0, NULL, FALSE);
+    sc_vtb_init(&scp->scr, VTB_FRAMEBUFFER, 0, 0, NULL, FALSE);
     scp->start = scp->xsize * scp->ysize - 1;
     scp->end = 0;
     scp->tsw = NULL;
@@ -3199,6 +3262,9 @@ sc_init_emulator(scr_stat *scp, char *name)
     if (strcmp(sw->te_renderer, "*") != 0) {
 	rndr = sc_render_match(scp, sw->te_renderer, scp->model);
     }
+    if (rndr == NULL && scp->sc->fbi != NULL) {
+	rndr = sc_render_match(scp, "kms", scp->model);
+    }
     if (rndr == NULL) {
 	rndr = sc_render_match(scp, scp->sc->adp->va_name, scp->model);
 	if (rndr == NULL) {
@@ -3628,7 +3694,7 @@ set_mode(scr_stat *scp)
 
     lwkt_gettoken(&tty_token);
     /* reject unsupported mode */
-    if ((*vidsw[scp->sc->adapter]->get_info)(scp->sc->adp, scp->mode, &info)) {
+    if (scp->sc->fbi == NULL && (*vidsw[scp->sc->adapter]->get_info)(scp->sc->adp, scp->mode, &info)) {
         lwkt_reltoken(&tty_token);
 	return 1;
     }
@@ -3640,9 +3706,12 @@ set_mode(scr_stat *scp)
     }
 
     /* setup video hardware for the given mode */
-    (*vidsw[scp->sc->adapter]->set_mode)(scp->sc->adp, scp->mode);
+    if (scp->sc->fbi == NULL)
+	(*vidsw[scp->sc->adapter]->set_mode)(scp->sc->adp, scp->mode);
     sc_vtb_init(&scp->scr, VTB_FRAMEBUFFER, scp->xsize, scp->ysize,
 		(void *)scp->sc->adp->va_window, FALSE);
+    if (scp->sc->fbi != NULL)
+	goto done;
 
 #ifndef SC_NO_FONT_LOADING
     /* load appropriate font */
@@ -3674,6 +3743,7 @@ set_mode(scr_stat *scp)
     sc_set_border(scp, scp->border);
     sc_set_cursor_image(scp);
 
+done:
     lwkt_reltoken(&tty_token);
     return 0;
 }
diff --git a/sys/dev/misc/syscons/syscons.h b/sys/dev/misc/syscons/syscons.h
index 93d0540..dbb80dd 100644
--- a/sys/dev/misc/syscons/syscons.h
+++ b/sys/dev/misc/syscons/syscons.h
@@ -29,7 +29,6 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/dev/syscons/syscons.h,v 1.60.2.6 2002/09/15 22:30:45 dd Exp $
- * $DragonFly: src/sys/dev/misc/syscons/syscons.h,v 1.21 2008/08/03 03:00:21 dillon Exp $
  */
 
 #ifndef _DEV_SYSCONS_SYSCONS_H_
@@ -199,6 +198,8 @@ typedef struct sc_softc {
 	struct video_adapter *adp;
 	int		initial_mode;		/* initial video mode */
 
+	struct fb_info	*fbi;
+
 	int		first_vty;
 	int		vtys;
 	cdev_t		*dev;
@@ -253,6 +254,7 @@ typedef struct scr_stat {
 	struct sc_rndr_sw *rndr;		/* renderer */
 	sc_vtb_t	scr;
 	sc_vtb_t	vtb;
+	struct fb_info	*fbi;
 
 	int 		xpos;			/* current X position */
 	int 		ypos;			/* current Y position */
@@ -260,11 +262,13 @@ typedef struct scr_stat {
 	int 		ysize;			/* Y text size */
 	int 		xpixel;			/* X graphics size */
 	int 		ypixel;			/* Y graphics size */
+	int		xpad;			/* for fbi->stride % 8 != 0 */
 	int		xoff;			/* X offset in pixel mode */
 	int		yoff;			/* Y offset in pixel mode */
 
 	u_char		*font;			/* current font */
 	int		font_size;		/* fontsize in Y direction */
+	int		font_mag;		/* font magnification */
 
 	int		start;			/* modified area start */
 	int		end;			/* modified area end */
@@ -570,6 +574,7 @@ int		sc_vid_ioctl(struct tty *tp, u_long cmd, caddr_t data,
 int		sc_render_add(sc_renderer_t *rndr);
 int		sc_render_remove(sc_renderer_t *rndr);
 sc_rndr_sw_t	*sc_render_match(scr_stat *scp, char *name, int model);
+void		sc_update_render(scr_stat *scp);
 
 /* scvtb.c */
 void		sc_vtb_init(sc_vtb_t *vtb, int type, int cols, int rows, 
diff --git a/sys/platform/pc64/conf/files b/sys/platform/pc64/conf/files
index 25aa8e4..b4bad61 100644
--- a/sys/platform/pc64/conf/files
+++ b/sys/platform/pc64/conf/files
@@ -92,6 +92,7 @@ dev/misc/kbd/kbdsw.c			optional	atkbd
 dev/misc/kbd/kbdsw.c			optional	sc
 dev/misc/kbd/kbdsw.c			optional	ukbd
 dev/misc/syscons/schistory.c		optional	sc
+dev/misc/syscons/sckmsrndr.c		optional	sc
 dev/misc/syscons/scmouse.c		optional	sc
 dev/misc/syscons/scterm.c		optional	sc
 dev/misc/syscons/scterm-dumb.c		optional	sc
-- 
2.4.0

