Commit 9069fd54 authored by Gerd Hoffmann's avatar Gerd Hoffmann Committed by Greg Kroah-Hartman

hyperv-fb: add support for generation 2 virtual machines.

UEFI-based generation 2 virtual machines support vmbus devices only.
There is no pci bus.  Thus they use a different mechanism for the
graphics framebuffer:  Instead of using the vga pci bar a chunk of
memory muct be allocated from the hyperv mmio region declared using
APCI.  This patch implements support for it.

Based on a patch by Haiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: default avatarGerd Hoffmann <kraxel@redhat.com>
Signed-off-by: default avatarHaiyang Zhang <haiyangz@microsoft.com>
Acked-by: default avatarTomi Valkeinen <tomi.valkeinen@ti.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 2da19688
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/fb.h> #include <linux/fb.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/efi.h>
#include <linux/hyperv.h> #include <linux/hyperv.h>
...@@ -212,6 +213,7 @@ struct synthvid_msg { ...@@ -212,6 +213,7 @@ struct synthvid_msg {
struct hvfb_par { struct hvfb_par {
struct fb_info *info; struct fb_info *info;
struct resource mem;
bool fb_ready; /* fb device is ready */ bool fb_ready; /* fb device is ready */
struct completion wait; struct completion wait;
u32 synthvid_version; u32 synthvid_version;
...@@ -460,13 +462,13 @@ static int synthvid_connect_vsp(struct hv_device *hdev) ...@@ -460,13 +462,13 @@ static int synthvid_connect_vsp(struct hv_device *hdev)
goto error; goto error;
} }
if (par->synthvid_version == SYNTHVID_VERSION_WIN7) { if (par->synthvid_version == SYNTHVID_VERSION_WIN7)
screen_depth = SYNTHVID_DEPTH_WIN7; screen_depth = SYNTHVID_DEPTH_WIN7;
screen_fb_size = SYNTHVID_FB_SIZE_WIN7; else
} else {
screen_depth = SYNTHVID_DEPTH_WIN8; screen_depth = SYNTHVID_DEPTH_WIN8;
screen_fb_size = SYNTHVID_FB_SIZE_WIN8;
} screen_fb_size = hdev->channel->offermsg.offer.
mmio_megabytes * 1024 * 1024;
return 0; return 0;
...@@ -627,26 +629,46 @@ static void hvfb_get_option(struct fb_info *info) ...@@ -627,26 +629,46 @@ static void hvfb_get_option(struct fb_info *info)
/* Get framebuffer memory from Hyper-V video pci space */ /* Get framebuffer memory from Hyper-V video pci space */
static int hvfb_getmem(struct fb_info *info) static int hvfb_getmem(struct fb_info *info)
{ {
struct pci_dev *pdev; struct hvfb_par *par = info->par;
ulong fb_phys; struct pci_dev *pdev = NULL;
void __iomem *fb_virt; void __iomem *fb_virt;
int gen2vm = efi_enabled(EFI_BOOT);
int ret;
pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT, par->mem.name = KBUILD_MODNAME;
par->mem.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
if (gen2vm) {
ret = allocate_resource(&hyperv_mmio, &par->mem,
screen_fb_size,
0, -1,
screen_fb_size,
NULL, NULL);
if (ret != 0) {
pr_err("Unable to allocate framebuffer memory\n");
return -ENODEV;
}
} else {
pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT,
PCI_DEVICE_ID_HYPERV_VIDEO, NULL); PCI_DEVICE_ID_HYPERV_VIDEO, NULL);
if (!pdev) { if (!pdev) {
pr_err("Unable to find PCI Hyper-V video\n"); pr_err("Unable to find PCI Hyper-V video\n");
return -ENODEV; return -ENODEV;
} }
if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM) || if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM) ||
pci_resource_len(pdev, 0) < screen_fb_size) pci_resource_len(pdev, 0) < screen_fb_size)
goto err1; goto err1;
fb_phys = pci_resource_end(pdev, 0) - screen_fb_size + 1; par->mem.end = pci_resource_end(pdev, 0);
if (!request_mem_region(fb_phys, screen_fb_size, KBUILD_MODNAME)) par->mem.start = par->mem.end - screen_fb_size + 1;
goto err1; ret = request_resource(&pdev->resource[0], &par->mem);
if (ret != 0) {
pr_err("Unable to request framebuffer memory\n");
goto err1;
}
}
fb_virt = ioremap(fb_phys, screen_fb_size); fb_virt = ioremap(par->mem.start, screen_fb_size);
if (!fb_virt) if (!fb_virt)
goto err2; goto err2;
...@@ -654,30 +676,42 @@ static int hvfb_getmem(struct fb_info *info) ...@@ -654,30 +676,42 @@ static int hvfb_getmem(struct fb_info *info)
if (!info->apertures) if (!info->apertures)
goto err3; goto err3;
info->apertures->ranges[0].base = pci_resource_start(pdev, 0); if (gen2vm) {
info->apertures->ranges[0].size = pci_resource_len(pdev, 0); info->apertures->ranges[0].base = screen_info.lfb_base;
info->fix.smem_start = fb_phys; info->apertures->ranges[0].size = screen_info.lfb_size;
} else {
info->apertures->ranges[0].base = pci_resource_start(pdev, 0);
info->apertures->ranges[0].size = pci_resource_len(pdev, 0);
}
info->fix.smem_start = par->mem.start;
info->fix.smem_len = screen_fb_size; info->fix.smem_len = screen_fb_size;
info->screen_base = fb_virt; info->screen_base = fb_virt;
info->screen_size = screen_fb_size; info->screen_size = screen_fb_size;
pci_dev_put(pdev); if (!gen2vm)
pci_dev_put(pdev);
return 0; return 0;
err3: err3:
iounmap(fb_virt); iounmap(fb_virt);
err2: err2:
release_mem_region(fb_phys, screen_fb_size); release_resource(&par->mem);
err1: err1:
pci_dev_put(pdev); if (!gen2vm)
pci_dev_put(pdev);
return -ENOMEM; return -ENOMEM;
} }
/* Release the framebuffer */ /* Release the framebuffer */
static void hvfb_putmem(struct fb_info *info) static void hvfb_putmem(struct fb_info *info)
{ {
struct hvfb_par *par = info->par;
iounmap(info->screen_base); iounmap(info->screen_base);
release_mem_region(info->fix.smem_start, screen_fb_size); release_resource(&par->mem);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment