diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2015-12-31 13:01:01 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2015-12-31 13:01:01 +0000 |
commit | abe7f2dd3289f65b15fe446e9f1597c6259a98e9 (patch) | |
tree | 79fc3d535f27524a1b628285746a0f48401173f2 /sys/dev/pci/drm | |
parent | 6bd5ffac6c54e8aed2204be323fdef13e0c0550b (diff) |
Provide a minimal implementation of the Linux vga_get/vga_put API and use it
in inteldrm(4).
The Intel integrated graphics device has a major design flaw where it needs
legacy VGA io access to disable VGA mode completely. This only works if
legacy VGA io routing is setup such that it actually reaches the IGD. This
typically isn't the case if the primary VGA device is a discrete graphics
device. To make sure we don't whack that device we have to temporarily
route legacy VGA io access to the IGD.
Fixes the "black screen" issue reported by Timo Myrra and others.
Diffstat (limited to 'sys/dev/pci/drm')
-rw-r--r-- | sys/dev/pci/drm/drm_drv.c | 3 | ||||
-rw-r--r-- | sys/dev/pci/drm/drm_linux.c | 75 | ||||
-rw-r--r-- | sys/dev/pci/drm/drm_linux.h | 8 | ||||
-rw-r--r-- | sys/dev/pci/drm/i915/intel_display.c | 8 | ||||
-rw-r--r-- | sys/dev/pci/drm/i915/intel_pm.c | 6 |
5 files changed, 90 insertions, 10 deletions
diff --git a/sys/dev/pci/drm/drm_drv.c b/sys/dev/pci/drm/drm_drv.c index 1f317bc07e0..867f8c56373 100644 --- a/sys/dev/pci/drm/drm_drv.c +++ b/sys/dev/pci/drm/drm_drv.c @@ -1,4 +1,4 @@ -/* $OpenBSD: drm_drv.c,v 1.141 2015/12/20 13:10:09 kettenis Exp $ */ +/* $OpenBSD: drm_drv.c,v 1.142 2015/12/31 13:01:00 kettenis Exp $ */ /*- * Copyright 2007-2009 Owain G. Ainsworth <oga@openbsd.org> * Copyright © 2008 Intel Corporation @@ -383,6 +383,7 @@ drm_attach(struct device *parent, struct device *self, void *aux) dev->pdev->pc = da->pc; dev->bridgetag = da->bridgetag; dev->pdev->tag = da->tag; + dev->pdev->pci = (struct pci_softc *)parent->dv_parent; rw_init(&dev->struct_mutex, "drmdevlk"); mtx_init(&dev->event_lock, IPL_TTY); diff --git a/sys/dev/pci/drm/drm_linux.c b/sys/dev/pci/drm/drm_linux.c index 4dcca39fdec..51f25d68dc3 100644 --- a/sys/dev/pci/drm/drm_linux.c +++ b/sys/dev/pci/drm/drm_linux.c @@ -1,4 +1,4 @@ -/* $OpenBSD: drm_linux.c,v 1.5 2015/09/26 11:17:15 kettenis Exp $ */ +/* $OpenBSD: drm_linux.c,v 1.6 2015/12/31 13:01:00 kettenis Exp $ */ /* * Copyright (c) 2013 Jonathan Gray <jsg@openbsd.org> * @@ -16,6 +16,7 @@ */ #include <dev/pci/drm/drmP.h> +#include <dev/pci/ppbreg.h> struct timespec ns_to_timespec(const int64_t nsec) @@ -207,3 +208,75 @@ vunmap(void *addr, size_t size) uvm_km_free(kernel_map, va, size); } +#if defined(__amd64__) || defined(__i386__) + +/* + * This is a minimal implementation of the Linux vga_get/vga_put + * interface. In all likelyhood, it will only work for inteldrm(4) as + * it assumes that if there is another active VGA device in the + * system, it is sitting behind a PCI bridge. + */ + +extern int pci_enumerate_bus(struct pci_softc *, + int (*)(struct pci_attach_args *), struct pci_attach_args *); + +pcitag_t vga_bridge_tag; +int vga_bridge_disabled; + +int +vga_disable_bridge(struct pci_attach_args *pa) +{ + pcireg_t bhlc, bc; + + if (pa->pa_domain != 0) + return 0; + + bhlc = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG); + if (PCI_HDRTYPE_TYPE(bhlc) != 1) + return 0; + + bc = pci_conf_read(pa->pa_pc, pa->pa_tag, PPB_REG_BRIDGECONTROL); + if ((bc & PPB_BC_VGA_ENABLE) == 0) + return 0; + bc &= ~PPB_BC_VGA_ENABLE; + pci_conf_write(pa->pa_pc, pa->pa_tag, PPB_REG_BRIDGECONTROL, bc); + + vga_bridge_tag = pa->pa_tag; + vga_bridge_disabled = 1; + + return 1; +} + +int +vga_enable_bridge(struct pci_attach_args *pa) +{ + pcireg_t bc; + + if (!vga_bridge_disabled) + return 0; + + if (pa->pa_tag != vga_bridge_tag) + return 0; + + bc = pci_conf_read(pa->pa_pc, pa->pa_tag, PPB_REG_BRIDGECONTROL); + bc |= PPB_BC_VGA_ENABLE; + pci_conf_write(pa->pa_pc, pa->pa_tag, PPB_REG_BRIDGECONTROL, bc); + + return 1; +} + +void +vga_get_uninterruptible(struct pci_dev *pdev, int rsrc) +{ + KASSERT(pdev->pci->sc_bridgetag == NULL); + pci_enumerate_bus(pdev->pci, vga_disable_bridge, NULL); +} + +void +vga_put(struct pci_dev *pdev, int rsrc) +{ + KASSERT(pdev->pci->sc_bridgetag == NULL); + pci_enumerate_bus(pdev->pci, vga_enable_bridge, NULL); +} + +#endif diff --git a/sys/dev/pci/drm/drm_linux.h b/sys/dev/pci/drm/drm_linux.h index 12d92618092..16d4e621df6 100644 --- a/sys/dev/pci/drm/drm_linux.h +++ b/sys/dev/pci/drm/drm_linux.h @@ -1,4 +1,4 @@ -/* $OpenBSD: drm_linux.h,v 1.42 2015/10/17 21:41:12 kettenis Exp $ */ +/* $OpenBSD: drm_linux.h,v 1.43 2015/12/31 13:01:00 kettenis Exp $ */ /* * Copyright (c) 2013, 2014, 2015 Mark Kettenis * @@ -982,6 +982,7 @@ struct pci_dev { uint16_t subsystem_device; pci_chipset_tag_t pc; pcitag_t tag; + struct pci_softc *pci; }; #define PCI_ANY_ID (uint16_t) (~0U) @@ -1079,6 +1080,11 @@ pci_dma_mapping_error(struct pci_dev *pdev, dma_addr_t dma_addr) return 0; } +#define VGA_RSRC_LEGACY_IO 0x01 + +void vga_get_uninterruptible(struct pci_dev *, int); +void vga_put(struct pci_dev *, int); + #endif #define memcpy_toio(d, s, n) memcpy(d, s, n) diff --git a/sys/dev/pci/drm/i915/intel_display.c b/sys/dev/pci/drm/i915/intel_display.c index f99866dcc8d..d2f12f05310 100644 --- a/sys/dev/pci/drm/i915/intel_display.c +++ b/sys/dev/pci/drm/i915/intel_display.c @@ -1,4 +1,4 @@ -/* $OpenBSD: intel_display.c,v 1.56 2015/09/25 09:42:14 kettenis Exp $ */ +/* $OpenBSD: intel_display.c,v 1.57 2015/12/31 13:01:00 kettenis Exp $ */ /* * Copyright © 2006-2007 Intel Corporation * @@ -10904,19 +10904,19 @@ static void i915_disable_vga(struct drm_device *dev) u8 sr1; u32 vga_reg = i915_vgacntrl_reg(dev); -#ifdef __linux__ vga_get_uninterruptible(dev->pdev, VGA_RSRC_LEGACY_IO); +#ifdef __linux__ outb(SR01, VGA_SR_INDEX); #else - outb(VGA_SR_INDEX,SR01); + outb(VGA_SR_INDEX, SR01); #endif sr1 = inb(VGA_SR_DATA); #ifdef __linux__ outb(sr1 | 1<<5, VGA_SR_DATA); - vga_put(dev->pdev, VGA_RSRC_LEGACY_IO); #else outb(VGA_SR_DATA, sr1 | 1<<5); #endif + vga_put(dev->pdev, VGA_RSRC_LEGACY_IO); udelay(300); I915_WRITE(vga_reg, VGA_DISP_DISABLE); diff --git a/sys/dev/pci/drm/i915/intel_pm.c b/sys/dev/pci/drm/i915/intel_pm.c index e6a3feb0d2d..9142bca63f8 100644 --- a/sys/dev/pci/drm/i915/intel_pm.c +++ b/sys/dev/pci/drm/i915/intel_pm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: intel_pm.c,v 1.39 2015/09/26 19:52:16 kettenis Exp $ */ +/* $OpenBSD: intel_pm.c,v 1.40 2015/12/31 13:01:00 kettenis Exp $ */ /* * Copyright © 2012 Intel Corporation * @@ -5216,13 +5216,13 @@ static void hsw_power_well_post_enable(struct drm_i915_private *dev_priv) * sure vgacon can keep working normally without triggering interrupts * and error messages. */ -#ifdef __linux__ vga_get_uninterruptible(dev->pdev, VGA_RSRC_LEGACY_IO); +#ifdef __linux__ outb(inb(VGA_MSR_READ), VGA_MSR_WRITE); - vga_put(dev->pdev, VGA_RSRC_LEGACY_IO); #else outb(VGA_MSR_WRITE, inb(VGA_MSR_READ)); #endif + vga_put(dev->pdev, VGA_RSRC_LEGACY_IO); if (IS_BROADWELL(dev)) { spin_lock_irqsave(&dev_priv->irq_lock, irqflags); |