public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Jon Smirl <jonsmirl@gmail.com>
To: Greg KH <greg@kroah.com>
Cc: Russell King <rmk+lkml@arm.linux.org.uk>,
	Jeff Garzik <jgarzik@pobox.com>, Matthew Wilcox <matthew@wil.cx>,
	Jesse Barnes <jbarnes@sgi.com>,
	linux-pci@atrey.karlin.mff.cuni.cz,
	lkml <linux-kernel@vger.kernel.org>
Subject: Re: Fwd: Patch to control VGA bus routing and active VGA device.
Date: Thu, 27 Jan 2005 04:59:45 -0500	[thread overview]
Message-ID: <9e473391050127015970e1fedc@mail.gmail.com> (raw)
In-Reply-To: <20050125042459.GA32697@kroah.com>

[-- Attachment #1: Type: text/plain, Size: 1214 bytes --]

On Mon, 24 Jan 2005 20:24:59 -0800, Greg KH <greg@kroah.com> wrote:
> This can be done today in the bus specific match functions.  And because
> of that, I would argue that this belongs in the bus specific code, and
> not in the driver core, as it's up to the bus to know what the different
> types of "matches" that can happen, and what the priority is.

Here's a version of the VGA control code that actually works. It helps
if I pci_enable() the device before using it. There was nothing wrong
with the routing code.

I can also control multiple cards on the same bus by turning on/off
their response to IO space and then enabling VGA via the standard
ports. I can use this to move my console from card to card.

I have this code in drivers/pci because it needs to know add/remove
from hotplug. Is there a better way to structure it? Note that this is
not a VGA device, it is just a mechanism for controlling which VGA
device is active.

Another item I need to add is generating an initial hotplug event for
each secondary card. This event has to happen even if there is a card
specific driver loaded. The event will be used to run the reset
program needed by secondary cards.

-- 
Jon Smirl
jonsmirl@gmail.com

[-- Attachment #2: patch --]
[-- Type: application/octet-stream, Size: 11001 bytes --]

diff -Nru a/arch/i386/pci/fixup.c b/arch/i386/pci/fixup.c
--- a/arch/i386/pci/fixup.c	2005-01-27 04:01:08 -05:00
+++ b/arch/i386/pci/fixup.c	2005-01-27 04:01:08 -05:00
@@ -375,6 +375,6 @@
 		}
 		bus = bus->parent;
 	}
-	pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW;
+	pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW | IORESOURCE_VGA_ACTIVE;
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_video);
diff -Nru a/drivers/pci/Kconfig b/drivers/pci/Kconfig
--- a/drivers/pci/Kconfig	2005-01-27 04:01:08 -05:00
+++ b/drivers/pci/Kconfig	2005-01-27 04:01:08 -05:00
@@ -47,3 +47,13 @@
 
 	  When in doubt, say Y.
 
+config VGA_CONTROL
+	bool "VGA Control"
+	depends on PCI
+	---help---
+	  Provides sysfs attributes for ensuring that only a single VGA
+	  device can be enabled per PCI domain. If a VGA device is removed
+	  via hotplug, display is routed to another VGA device if available.
+
+	  If you have more than one VGA device, say Y.
+
diff -Nru a/drivers/pci/Makefile b/drivers/pci/Makefile
--- a/drivers/pci/Makefile	2005-01-27 04:01:08 -05:00
+++ b/drivers/pci/Makefile	2005-01-27 04:01:08 -05:00
@@ -28,6 +28,7 @@
 obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o
 obj-$(CONFIG_X86_VISWS) += setup-irq.o
 obj-$(CONFIG_PCI_MSI) += msi.o
+obj-$(CONFIG_VGA_CONTROL) += vga.o
 
 #
 # ACPI Related PCI FW Functions
diff -Nru a/drivers/pci/bus.c b/drivers/pci/bus.c
--- a/drivers/pci/bus.c	2005-01-27 04:01:08 -05:00
+++ b/drivers/pci/bus.c	2005-01-27 04:01:08 -05:00
@@ -85,6 +85,9 @@
 
 	pci_proc_attach_device(dev);
 	pci_create_sysfs_dev_files(dev);
+#if CONFIG_VGA_CONTROL
+	pci_vga_add_device(dev);
+#endif
 }
 
 /**
diff -Nru a/drivers/pci/pci.h b/drivers/pci/pci.h
--- a/drivers/pci/pci.h	2005-01-27 04:01:08 -05:00
+++ b/drivers/pci/pci.h	2005-01-27 04:01:08 -05:00
@@ -11,6 +11,8 @@
 				  void (*alignf)(void *, struct resource *,
 					  	 unsigned long, unsigned long),
 				  void *alignf_data);
+extern int pci_vga_add_device(struct pci_dev *pdev);
+extern int pci_vga_remove_device(struct pci_dev *pdev);
 /* PCI /proc functions */
 #ifdef CONFIG_PROC_FS
 extern int pci_proc_attach_device(struct pci_dev *dev);
diff -Nru a/drivers/pci/remove.c b/drivers/pci/remove.c
--- a/drivers/pci/remove.c	2005-01-27 04:01:08 -05:00
+++ b/drivers/pci/remove.c	2005-01-27 04:01:08 -05:00
@@ -26,6 +26,9 @@
 
 static void pci_destroy_dev(struct pci_dev *dev)
 {
+#if CONFIG_VGA_CONTROL
+	pci_vga_remove_device(dev);
+#endif
 	pci_proc_detach_device(dev);
 	pci_remove_sysfs_dev_files(dev);
 	device_unregister(&dev->dev);
diff -Nru a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
--- a/drivers/pci/setup-bus.c	2005-01-27 04:01:08 -05:00
+++ b/drivers/pci/setup-bus.c	2005-01-27 04:01:08 -05:00
@@ -64,7 +64,9 @@
 
 		if (class == PCI_CLASS_DISPLAY_VGA ||
 		    class == PCI_CLASS_NOT_DEFINED_VGA)
-			bus->bridge_ctl |= PCI_BRIDGE_CTL_VGA;
+			/* only route to the active VGA, ignore inactive ones */
+			if  (dev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_VGA_ACTIVE)
+				bus->bridge_ctl |= PCI_BRIDGE_CTL_VGA;
 
 		pdev_sort_resources(dev, &head);
 	}
diff -Nru a/drivers/pci/vga.c b/drivers/pci/vga.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/pci/vga.c	2005-01-27 04:01:08 -05:00
@@ -0,0 +1,254 @@
+/*
+ * linux/drivers/pci/vga.c
+ *
+ * (C) Copyright 2004 Jon Smirl <jonsmirl@gmail.com>
+ *
+ * VGA control logic for ensuring only a single enabled VGA device
+ */
+
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/pci.h>
+#include <linux/major.h>
+
+static int vga_initialized = 0;
+static struct pci_dev *vga_active = NULL;
+
+static void bridge_yes(struct pci_dev *pdev)
+{
+	struct pci_dev *bridge;
+	struct pci_bus *bus;
+	
+	/* Make sure the bridges route to us */
+	bus = pdev->bus;
+	while (bus) {
+		bridge = bus->self;
+		if (bridge) {
+			bus->bridge_ctl |= PCI_BRIDGE_CTL_VGA;
+			pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl);
+		}
+		bus = bus->parent;
+	}
+}
+
+static void bridge_no(struct pci_dev *pdev)
+{
+	struct pci_dev *bridge;
+	struct pci_bus *bus;
+	
+	/* Make sure the bridges don't route to us */
+	bus = pdev->bus;
+	while (bus) {
+		bridge = bus->self;
+		if (bridge) {
+			bus->bridge_ctl &= ~PCI_BRIDGE_CTL_VGA;
+			pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl);
+		}
+		bus = bus->parent;
+	}
+}
+
+static void vga_enable(struct pci_dev *pdev, unsigned int enable)
+{
+	u16 command;
+	
+	bridge_yes(pdev);
+
+	if (enable) {
+		pci_enable_device(pdev);
+		/* this assumes all other potential VGA devices are disabled */
+		outb(0x01 | inb(0x3C3),  0x3C3);  /* 0 - enable */
+		outb(0x08 | inb(0x46e8), 0x46e8);
+		outb(0x01 | inb(0x102),  0x102);
+		pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_VGA_ACTIVE;
+		vga_active = pdev;
+
+		/* return and leave the card enabled */
+		return;
+	}
+	
+	pci_read_config_word(pdev, PCI_COMMAND, &command);
+	pci_write_config_word(pdev, PCI_COMMAND, command | PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+	
+	outb(~0x01 & inb(0x3C3),  0x3C3);
+	outb(~0x08 & inb(0x46e8), 0x46e8);
+	outb(~0x01 & inb(0x102),  0x102);
+	pdev->resource[PCI_ROM_RESOURCE].flags &= ~IORESOURCE_VGA_ACTIVE;
+	if (pdev == vga_active)
+		vga_active = NULL;
+	bridge_no(pdev);
+
+	pci_write_config_word(pdev, PCI_COMMAND, command);
+}
+
+/* echo these values to the sysfs vga attribute on a VGA device */
+enum eEnable {
+	VGA_DISABLE_THIS = 0,	/* If this VGA is enabled, disable it. */
+	VGA_ENABLE_THIS = 1,	/* Disable all VGAs then enable this VGA, mark as active VGA */
+	/* Used while resetting a board, board being reset may not be the active VGA */
+	VGA_DISABLE_ALL = 2,	/* Remember active VGA then disable all VGAa and devices */
+	VGA_ENABLE_ACTIVE = 3,	/* Make sure all VGAs are disabled, then reenable active VGA */
+};
+
+static void set_state(struct pci_dev *pdev, enum eEnable enable)
+{
+	struct pci_dev *pcidev = NULL;
+	unsigned int class;
+
+	if (enable == VGA_DISABLE_THIS)
+		if (vga_active != pdev)
+			return;
+		
+	vga_enable(vga_active, 0);
+
+	/* loop over all devices and make sure no multiple routings */
+	while ((pcidev = pci_get_subsys(PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pcidev)) != NULL) {
+		class = pcidev->class >> 8;
+
+		if (class == PCI_CLASS_DISPLAY_VGA)
+			bridge_no(pcidev);
+	}
+
+	/* loop over all devices and make sure everyone is disabled */
+	while ((pcidev = pci_get_subsys(PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pcidev)) != NULL) {
+		class = pcidev->class >> 8;
+
+		if (class == PCI_CLASS_DISPLAY_VGA)
+			vga_enable(pcidev, 0);
+	}
+
+	switch (enable) {
+		case VGA_DISABLE_THIS:
+		case VGA_DISABLE_ALL:
+			break;
+
+		/* Mark us active if requested */
+		case VGA_ENABLE_THIS:
+			vga_enable(pdev, 1);
+			break;
+	
+		/* Restore active device if requested */
+		case VGA_ENABLE_ACTIVE:
+			vga_enable(vga_active, 1);
+			break;
+	}
+}
+
+/* sysfs store for VGA device */
+static ssize_t vga_device_store(struct device *dev, const char *buf, size_t count)
+{
+	char *last;
+	struct pci_dev *pdev = to_pci_dev(dev);
+	enum eEnable enable;
+
+	/* sysfs strings are terminated by \n */
+	enable = simple_strtoul(buf, &last, 0);
+	if (last == buf)
+		return -EINVAL;
+
+	if ((enable < VGA_DISABLE_THIS) || (enable > VGA_ENABLE_ACTIVE))
+		return -EINVAL;
+
+	set_state(pdev, enable);
+
+	return count;
+}
+
+/* sysfs show for VGA device */
+static ssize_t vga_device_show(struct device *dev, char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	return sprintf(buf, "%d\n", (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_VGA_ACTIVE) != 0);
+}
+
+static struct device_attribute vga_device_attr = __ATTR(vga, S_IRUGO|S_IWUSR, vga_device_show, vga_device_store);
+
+/* sysfs show for VGA routing bridge */
+static ssize_t vga_bridge_show(struct device *dev, char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	u16 l;
+
+	/* don't trust the shadow PCI_BRIDGE_CTL_VGA in pdev */
+	/* user space may change hardware without telling the kernel */
+	pci_read_config_word(pdev, PCI_BRIDGE_CONTROL, &l);
+	return sprintf(buf, "%d\n", (l & PCI_BRIDGE_CTL_VGA) != 0);
+}
+
+static struct device_attribute vga_bridge_attr = __ATTR(vga, S_IRUGO, vga_bridge_show, NULL);
+
+/* If the device is a VGA or a bridge, add a VGA sysfs attribute */
+int pci_vga_add_device(struct pci_dev *pdev)
+{
+	int class = pdev->class >> 8;
+
+	if (!vga_initialized)
+		return -EACCES;
+
+	if (class == PCI_CLASS_DISPLAY_VGA) {
+		device_create_file(&pdev->dev, &vga_device_attr);
+
+		/* record the active boot device when located */
+		if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_VGA_ACTIVE)
+			vga_active = pdev;
+		return 0;
+	}
+
+	if ((class == PCI_CLASS_BRIDGE_PCI) || (class == PCI_CLASS_BRIDGE_CARDBUS)) {
+		device_create_file(&pdev->dev, &vga_bridge_attr);
+	}
+	return 0;
+}
+
+/* If the device is a VGA or a bridge, remove the VGA sysfs attribute */
+int pci_vga_remove_device(struct pci_dev *pdev)
+{
+	struct pci_dev *pcidev = NULL;
+	int class = pdev->class >> 8;
+
+	if (!vga_initialized)
+		return -EACCES;
+
+	if (class == PCI_CLASS_DISPLAY_VGA) {
+		device_remove_file(&pdev->dev, &vga_device_attr);
+
+		/* record the active boot device when located */
+		if (vga_active == pdev) {
+			while ((pcidev = pci_get_subsys(PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pcidev)) != NULL) {
+				class = pcidev->class >> 8;
+				if (class != PCI_CLASS_DISPLAY_VGA)
+					continue;
+				if (pcidev == pdev)
+					continue;
+				set_state(pcidev, VGA_ENABLE_THIS);
+				break;
+			}
+			if (pcidev == NULL)
+				set_state(NULL, VGA_DISABLE_ALL);
+			
+		}
+		return 0;
+	}
+
+	if ((class == PCI_CLASS_BRIDGE_PCI) || (class == PCI_CLASS_BRIDGE_CARDBUS))
+		device_remove_file(&pdev->dev, &vga_bridge_attr);
+
+	return 0;
+}
+
+/* Initialize by scanning all devices */
+static int __init vga_init(void)
+{
+	struct pci_dev *pdev = NULL;
+
+	vga_initialized = 1;
+
+	while ((pdev = pci_get_subsys(PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL)
+		pci_vga_add_device(pdev);
+		
+	return 0;
+}
+
+__initcall(vga_init);
+
diff -Nru a/include/linux/ioport.h b/include/linux/ioport.h
--- a/include/linux/ioport.h	2005-01-27 04:01:08 -05:00
+++ b/include/linux/ioport.h	2005-01-27 04:01:08 -05:00
@@ -41,7 +41,6 @@
 #define IORESOURCE_CACHEABLE	0x00004000
 #define IORESOURCE_RANGELENGTH	0x00008000
 #define IORESOURCE_SHADOWABLE	0x00010000
-#define IORESOURCE_BUS_HAS_VGA	0x00080000
 
 #define IORESOURCE_DISABLED	0x10000000
 #define IORESOURCE_UNSET	0x20000000
@@ -86,6 +85,7 @@
 #define IORESOURCE_ROM_ENABLE		(1<<0)	/* ROM is enabled, same as PCI_ROM_ADDRESS_ENABLE */
 #define IORESOURCE_ROM_SHADOW		(1<<1)	/* ROM is copy at C000:0 */
 #define IORESOURCE_ROM_COPY		(1<<2)	/* ROM is alloc'd copy, resource field overlaid */
+#define IORESOURCE_VGA_ACTIVE		(1<<3)	/* VGA device is active */
 
 /* PC/ISA/whatever - the normal PC address spaces: IO and memory */
 extern struct resource ioport_resource;

  reply	other threads:[~2005-01-27 10:00 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2005-01-18  3:43 Patch to control VGA bus routing and active VGA device Jon Smirl
2005-01-18 17:46 ` Jesse Barnes
2005-01-18 19:38   ` H. Peter Anvin
2005-01-18 21:06     ` Jesse Barnes
2005-01-22 19:04       ` Jon Smirl
2005-01-24 17:25         ` Jesse Barnes
2005-01-24 17:53           ` Jesse Barnes
     [not found] ` <41ED3BD2.1090105@pobox.com>
     [not found]   ` <9e473391050122083822a7f81c@mail.gmail.com>
     [not found]     ` <200501240847.51208.jbarnes@sgi.com>
     [not found]       ` <20050124175131.GM31455@parcelfarce.linux.theplanet.co.uk>
2005-01-24 19:17         ` Fwd: " Jon Smirl
2005-01-24 19:42           ` Jeff Garzik
2005-01-24 19:55             ` Russell King
2005-01-24 23:11               ` Jon Smirl
2005-01-25  4:24               ` Greg KH
2005-01-27  9:59                 ` Jon Smirl [this message]
2005-01-27 16:28                   ` Jesse Barnes
2005-01-28 17:32                     ` Grant Grundler
2005-01-28 18:36                       ` Jon Smirl
2005-01-28 19:15                         ` Grant Grundler
2005-01-28 19:26                           ` Jon Smirl
2005-01-28 19:34                             ` Grant Grundler
2005-01-28 18:41                       ` Jesse Barnes
2005-01-28 19:33                         ` Grant Grundler
2005-01-28 19:41                           ` Jesse Barnes
2005-01-28 20:12                             ` Grant Grundler
2005-01-28 20:00                           ` Matthew Wilcox
2005-01-28 20:07                             ` Russell King
2005-01-31 16:01                             ` Alan Cox
2005-02-01  6:38                   ` Greg KH
2005-02-01 16:24                     ` Jon Smirl
2005-01-30  7:51                 ` Jon Smirl
2005-01-24 20:14             ` Matthew Wilcox
2005-01-24 20:22           ` Matthew Wilcox

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=9e473391050127015970e1fedc@mail.gmail.com \
    --to=jonsmirl@gmail.com \
    --cc=greg@kroah.com \
    --cc=jbarnes@sgi.com \
    --cc=jgarzik@pobox.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@atrey.karlin.mff.cuni.cz \
    --cc=matthew@wil.cx \
    --cc=rmk+lkml@arm.linux.org.uk \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox