From: Gerd Hoffmann <kraxel@redhat.com>
To: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] vga-pci and MMIO BAR
Date: Mon, 06 Aug 2012 14:03:59 +0200 [thread overview]
Message-ID: <501FB2AF.9040908@redhat.com> (raw)
In-Reply-To: <1343188660.3715.41.camel@pasglop>
[-- Attachment #1: Type: text/plain, Size: 693 bytes --]
On 07/25/12 05:57, Benjamin Herrenschmidt wrote:
> Hi folks !
>
> Would there be any objection to adding a second MMIO BAR to qemu-vga
> which mirrors the bochs magic VBE ports ?
No.
> Once done, I'd like to look into doing a qemudrmfb similar to the cirrus
> one that pretty much gives you generic KMS support (with RandR) on top
> of vga-pci. This would have the advantage of potentially lifting the
> pitch and size limitations that plague cirrus.
Hacked up something like that a while back (patch attached). Doesn't
use DRM though, just plain old fbdev. Maybe you can reuse some bits
nevertheless.
/me wonders what the advantage of using drm is for non-3D hardware?
cheers,
Gerd
[-- Attachment #2: 0001-add-bochs-dispi-interface-framebuffer-driver.patch --]
[-- Type: text/plain, Size: 13039 bytes --]
From 637f637df63a2fc676d7fa09c4fec40172559bab Mon Sep 17 00:00:00 2001
From: Gerd Hoffmann <kraxel@redhat.com>
Date: Tue, 6 Mar 2012 14:34:38 +0100
Subject: [PATCH] add bochs dispi interface framebuffer driver
This patchs adds a frame buffer driver for (virtual/emulated) vga cards
implementing the bochs dispi interface. Supported hardware are the
bochs vga card with vbe extension and the qemu standard vga.
The driver uses a fixed depth of 32bpp. Otherwise it supports the full
(but small) feature set of the bochs dispi interface: Resolution
switching and display panning. It is tweaked to maximize fbcon speed,
so you'll get the comfort of the framebuffer console in kvm guests
without performance penalty.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
drivers/video/Kconfig | 18 +++
drivers/video/Makefile | 1 +
drivers/video/bochsfb.c | 385 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 404 insertions(+), 0 deletions(-)
create mode 100644 drivers/video/bochsfb.c
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 0217f74..cf401ce 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -286,6 +286,24 @@ config FB_CIRRUS
Say N unless you have such a graphics board or plan to get one
before you next recompile the kernel.
+config FB_BOCHS
+ tristate "Bochs dispi interface support"
+ depends on FB && PCI
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ ---help---
+ This is the frame buffer driver for (virtual/emulated) vga
+ cards implementing the bochs dispi interface. Supported
+ hardware are the bochs vga card with vbe extension and the
+ qemu standard vga.
+
+ The driver handles the PCI variants only. It uses a fixed
+ depth of 32bpp, anything else doesn't make sense these days.
+
+ Say Y here if you plan to run the kernel in a virtual machine
+ emulated by bochs or qemu.
+
config FB_PM2
tristate "Permedia2 support"
depends on FB && ((AMIGA && BROKEN) || PCI)
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index ee8dafb..81b78f1 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -102,6 +102,7 @@ obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o
obj-$(CONFIG_FB_68328) += 68328fb.o
obj-$(CONFIG_FB_GBE) += gbefb.o
obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o
+obj-$(CONFIG_FB_BOCHS) += bochsfb.o
obj-$(CONFIG_FB_ASILIANT) += asiliantfb.o
obj-$(CONFIG_FB_PXA) += pxafb.o
obj-$(CONFIG_FB_PXA168) += pxa168fb.o
diff --git a/drivers/video/bochsfb.c b/drivers/video/bochsfb.c
new file mode 100644
index 0000000..18a94dc
--- /dev/null
+++ b/drivers/video/bochsfb.c
@@ -0,0 +1,385 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/pm.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/console.h>
+#include <asm/io.h>
+
+#define VBE_DISPI_IOPORT_INDEX 0x01CE
+#define VBE_DISPI_IOPORT_DATA 0x01CF
+
+#define VBE_DISPI_INDEX_ID 0x0
+#define VBE_DISPI_INDEX_XRES 0x1
+#define VBE_DISPI_INDEX_YRES 0x2
+#define VBE_DISPI_INDEX_BPP 0x3
+#define VBE_DISPI_INDEX_ENABLE 0x4
+#define VBE_DISPI_INDEX_BANK 0x5
+#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6
+#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7
+#define VBE_DISPI_INDEX_X_OFFSET 0x8
+#define VBE_DISPI_INDEX_Y_OFFSET 0x9
+#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa
+
+#define VBE_DISPI_ID0 0xB0C0
+#define VBE_DISPI_ID1 0xB0C1
+#define VBE_DISPI_ID2 0xB0C2
+#define VBE_DISPI_ID3 0xB0C3
+#define VBE_DISPI_ID4 0xB0C4
+#define VBE_DISPI_ID5 0xB0C5
+
+#define VBE_DISPI_DISABLED 0x00
+#define VBE_DISPI_ENABLED 0x01
+#define VBE_DISPI_GETCAPS 0x02
+#define VBE_DISPI_8BIT_DAC 0x20
+#define VBE_DISPI_LFB_ENABLED 0x40
+#define VBE_DISPI_NOCLEARMEM 0x80
+
+enum bochs_types {
+ BOCHS_QEMU_STDVGA,
+ BOCHS_UNKNOWN,
+};
+
+static const char *bochs_names[] = {
+ [ BOCHS_QEMU_STDVGA ] = "QEMU standard vga",
+ [ BOCHS_UNKNOWN ] = "unknown",
+};
+
+static struct fb_fix_screeninfo bochsfb_fix __devinitdata = {
+ .id = "bochsfb",
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_TRUECOLOR,
+ .accel = FB_ACCEL_NONE,
+ .xpanstep = 1,
+ .ypanstep = 1,
+};
+
+static struct fb_var_screeninfo bochsfb_var __devinitdata = {
+ .xres = 1024,
+ .yres = 768,
+ .bits_per_pixel = 32,
+#ifdef __BIG_ENDIAN
+ .transp = { .length = 8, .offset = 0 },
+ .red = { .length = 8, .offset = 8 },
+ .green = { .length = 8, .offset = 16 },
+ .blue = { .length = 8, .offset = 24 },
+#else
+ .transp = { .length = 8, .offset = 24 },
+ .red = { .length = 8, .offset = 16 },
+ .green = { .length = 8, .offset = 8 },
+ .blue = { .length = 8, .offset = 0 },
+#endif
+ .height = -1,
+ .width = -1,
+ .vmode = FB_VMODE_NONINTERLACED,
+ .pixclock = 10000,
+ .left_margin = 16,
+ .right_margin = 16,
+ .upper_margin = 16,
+ .lower_margin = 16,
+ .hsync_len = 8,
+ .vsync_len = 8,
+};
+
+static char *mode __devinitdata;
+module_param(mode, charp, 0);
+MODULE_PARM_DESC(mode, "Initial video mode e.g. '648x480'");
+
+static u16 bochs_read(u16 reg)
+{
+ outw(reg, VBE_DISPI_IOPORT_INDEX);
+ return inw(VBE_DISPI_IOPORT_DATA);
+}
+
+static void bochs_write(u16 reg, u16 val)
+{
+ outw(reg, VBE_DISPI_IOPORT_INDEX);
+ outw(val, VBE_DISPI_IOPORT_DATA);
+}
+
+static int bochsfb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ uint32_t x,y, xv,yv, pixels;
+
+ if (var->bits_per_pixel != 32 ||
+ var->xres > 65535 ||
+ var->xres_virtual > 65535 ||
+ (var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
+ return -EINVAL;
+
+ x = var->xres & ~0x0f;
+ y = var->yres & ~0x03;
+ xv = var->xres_virtual & ~0x0f;
+ yv = var->yres_virtual & ~0x03;
+ if (xv < x)
+ xv = x;
+ pixels = info->fix.smem_len * 8 / info->var.bits_per_pixel;
+ yv = pixels / xv;
+ if (y > yv)
+ return -EINVAL;
+
+ var->xres = x;
+ var->yres = y;
+ var->xres_virtual = xv;
+ var->yres_virtual = yv;
+ var->xoffset = 0;
+ var->yoffset = 0;
+
+ return 0;
+}
+
+static int bochsfb_set_par(struct fb_info *info)
+{
+ dev_dbg(info->dev, "set mode: real: %dx%d, virtual: %dx%d\n",
+ info->var.xres, info->var.yres,
+ info->var.xres_virtual, info->var.yres_virtual);
+
+ info->fix.line_length = info->var.xres * info->var.bits_per_pixel / 8;
+
+ bochs_write(VBE_DISPI_INDEX_BPP, info->var.bits_per_pixel);
+ bochs_write(VBE_DISPI_INDEX_XRES, info->var.xres);
+ bochs_write(VBE_DISPI_INDEX_YRES, info->var.yres);
+ bochs_write(VBE_DISPI_INDEX_BANK, 0);
+ bochs_write(VBE_DISPI_INDEX_VIRT_WIDTH, info->var.xres_virtual);
+ bochs_write(VBE_DISPI_INDEX_VIRT_HEIGHT, info->var.yres_virtual);
+ bochs_write(VBE_DISPI_INDEX_X_OFFSET, info->var.xoffset);
+ bochs_write(VBE_DISPI_INDEX_Y_OFFSET, info->var.yoffset);
+
+ bochs_write(VBE_DISPI_INDEX_ENABLE,
+ VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED);
+ return 0;
+}
+
+static int bochsfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp,
+ struct fb_info *info)
+{
+ if (regno < 16 && info->var.bits_per_pixel == 32) {
+ red >>= 8;
+ green >>= 8;
+ blue >>= 8;
+ ((u32 *)(info->pseudo_palette))[regno] =
+ (red << info->var.red.offset) |
+ (green << info->var.green.offset) |
+ (blue << info->var.blue.offset);
+ }
+ return 0;
+}
+
+static int bochsfb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ bochs_write(VBE_DISPI_INDEX_X_OFFSET, var->xoffset);
+ bochs_write(VBE_DISPI_INDEX_Y_OFFSET, var->yoffset);
+ return 0;
+}
+
+static struct fb_ops bochsfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = bochsfb_check_var,
+ .fb_set_par = bochsfb_set_par,
+ .fb_setcolreg = bochsfb_setcolreg,
+ .fb_pan_display = bochsfb_pan_display,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+static int __devinit
+bochsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent)
+{
+ struct fb_info *p;
+ unsigned long addr, size, mem;
+ u16 id;
+ int rc = -ENODEV;
+
+ id = bochs_read(VBE_DISPI_INDEX_ID);;
+ mem = bochs_read(VBE_DISPI_INDEX_VIDEO_MEMORY_64K) * 64 * 1024;
+ dev_info(&dp->dev,"Found bochs VGA, ID 0x%x, mem %ldk, type \"%s\".\n",
+ id, mem / 1024, bochs_names[ent->driver_data]);
+ if ((id & 0xfff0) != VBE_DISPI_ID0) {
+ dev_err(&dp->dev, "ID mismatch\n");
+ goto err_out;
+ }
+
+ if (pci_enable_device(dp) < 0) {
+ dev_err(&dp->dev, "Cannot enable PCI device\n");
+ goto err_out;
+ }
+
+ if ((dp->resource[0].flags & IORESOURCE_MEM) == 0)
+ goto err_disable;
+ addr = pci_resource_start(dp, 0);
+ size = pci_resource_len(dp, 0);
+ if (addr == 0)
+ goto err_disable;
+ if (size != mem) {
+ dev_err(&dp->dev, "Size mismatch: pci=%ld, bochs=%ld\n", size, mem);
+ size = min(size, mem);
+ }
+
+ p = framebuffer_alloc(0, &dp->dev);
+ if (p == NULL) {
+ dev_err(&dp->dev, "Cannot allocate framebuffer structure\n");
+ rc = -ENOMEM;
+ goto err_disable;
+ }
+
+ if (pci_request_region(dp, 0, "bochsfb") != 0) {
+ dev_err(&dp->dev, "Cannot request framebuffer\n");
+ rc = -EBUSY;
+ goto err_release_fb;
+ }
+
+ if (!request_region(VBE_DISPI_IOPORT_INDEX, 2, "bochsfb")) {
+ dev_err(&dp->dev, "Cannot request ioports\n");
+ rc = -EBUSY;
+ goto err_release_pci;
+ }
+
+ p->screen_base = ioremap(addr, size);
+ if (p->screen_base == NULL) {
+ dev_err(&dp->dev, "Cannot map framebuffer\n");
+ rc = -ENOMEM;
+ goto err_release_ports;
+ }
+ memset(p->screen_base, 0, size);
+
+ pci_set_drvdata(dp, p);
+ p->fbops = &bochsfb_ops;
+ p->flags = FBINFO_FLAG_DEFAULT
+ | FBINFO_READS_FAST
+ | FBINFO_HWACCEL_XPAN
+ | FBINFO_HWACCEL_YPAN;
+ p->pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
+ p->fix = bochsfb_fix;
+ p->fix.smem_start = addr;
+ p->fix.smem_len = size;
+
+ p->var = bochsfb_var;
+ bochsfb_check_var(&p->var, p);
+ if (mode) {
+ fb_find_mode(&p->var, p, mode, NULL, 0, NULL, 32);
+ }
+
+ if (register_framebuffer(p) < 0) {
+ dev_err(&dp->dev,"Framebuffer failed to register\n");
+ goto err_unmap;
+ }
+
+ dev_info(&dp->dev,"fb%d: bochs VGA frame buffer initialized.\n",
+ p->node);
+
+ return 0;
+
+ err_unmap:
+ iounmap(p->screen_base);
+ err_release_ports:
+ release_region(VBE_DISPI_IOPORT_INDEX, 2);
+ err_release_pci:
+ pci_release_region(dp, 0);
+ err_release_fb:
+ framebuffer_release(p);
+ err_disable:
+ err_out:
+ return rc;
+}
+
+static void __devexit bochsfb_remove(struct pci_dev *dp)
+{
+ struct fb_info *p = pci_get_drvdata(dp);
+
+ if (p->screen_base == NULL)
+ return;
+ unregister_framebuffer(p);
+ iounmap(p->screen_base);
+ p->screen_base = NULL;
+ release_region(VBE_DISPI_IOPORT_INDEX, 2);
+ pci_release_region(dp, 0);
+ kfree(p->pseudo_palette);
+ framebuffer_release(p);
+}
+
+static struct pci_device_id bochsfb_pci_tbl[] = {
+ {
+ .vendor = 0x1234,
+ .device = 0x1111,
+ .subvendor = 0x1af4,
+ .subdevice = 0x1100,
+ .driver_data = BOCHS_QEMU_STDVGA,
+ },
+ {
+ .vendor = 0x1234,
+ .device = 0x1111,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = BOCHS_UNKNOWN,
+ },
+ { /* end of list */ }
+};
+
+MODULE_DEVICE_TABLE(pci, bochsfb_pci_tbl);
+
+static struct pci_driver bochsfb_driver = {
+ .name = "bochsfb",
+ .id_table = bochsfb_pci_tbl,
+ .probe = bochsfb_pci_init,
+ .remove = __devexit_p(bochsfb_remove),
+};
+
+#ifndef MODULE
+static int __init bochsfb_setup(char *options)
+{
+ char *this_opt;
+
+ if (!options || !*options)
+ return 0;
+
+ while ((this_opt = strsep(&options, ",")) != NULL) {
+ if (!*this_opt)
+ continue;
+ if (!strncmp(this_opt, "mode:", 5))
+ mode = this_opt + 5;
+ else
+ mode = this_opt;
+ }
+ return 0;
+}
+#endif
+
+int __init bochs_init(void)
+{
+#ifndef MODULE
+ char *option = NULL;
+
+ if (fb_get_options("bochsfb", &option))
+ return -ENODEV;
+ bochsfb_setup(option);
+#endif
+ return pci_register_driver(&bochsfb_driver);
+}
+
+module_init(bochs_init);
+
+static void __exit bochsfb_exit(void)
+{
+ pci_unregister_driver(&bochsfb_driver);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>");
+MODULE_DESCRIPTION("bochs dispi interface framebuffer driver");
--
1.7.1
next prev parent reply other threads:[~2012-08-06 12:04 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-07-25 3:57 [Qemu-devel] vga-pci and MMIO BAR Benjamin Herrenschmidt
2012-07-25 10:27 ` Avi Kivity
2012-07-25 10:53 ` Benjamin Herrenschmidt
2012-07-25 12:48 ` Avi Kivity
2012-07-25 13:41 ` Benjamin Herrenschmidt
2012-07-25 13:59 ` Avi Kivity
2012-07-25 22:01 ` Benjamin Herrenschmidt
2012-07-26 0:22 ` Benjamin Herrenschmidt
2012-07-26 1:08 ` Anthony Liguori
2012-07-26 1:18 ` Benjamin Herrenschmidt
2012-08-06 12:03 ` Gerd Hoffmann [this message]
2012-08-06 21:07 ` Benjamin Herrenschmidt
2012-08-10 15:27 ` Alon Levy
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=501FB2AF.9040908@redhat.com \
--to=kraxel@redhat.com \
--cc=benh@kernel.crashing.org \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.