Fixing X220 Video The Right Way

John Baldwin jhb at freebsd.org
Thu Feb 28 17:09:56 UTC 2013


On Thursday, February 28, 2013 8:15:46 am matt wrote:
> On 02/27/13 12:27, John Baldwin wrote:
> > On Wednesday, February 27, 2013 1:35:43 pm matt wrote:
> >> On 02/27/13 09:00, John Baldwin wrote:
> >>> If that is true, it's because your BIOS is lying. Do you have a URL to
> >>> your ASL lying around already?
> >> Too big for pastebin :( +500k
> >>
> >> https://docs.google.com/file/d/0B6YlMzJxarGbVnotLUdNWWNTVG8/edit?usp=sharing
> > Here is where I find _DOD and _DOS methods:
> >
> >          Device (PCI0)
> >              Device (VID)
> >                  Name (_ADR, 0x00020000)  // _ADR: Address
> >                  Method (_DOS, 1, NotSerialized)  // _DOS: Disable Output Switching
> >                  Method (_DOD, 0, NotSerialized)  // _DOD: Display Output Devices
> >              Device (PEG)
> >                  Name (_ADR, 0x00010000)  // _ADR: Address
> >                  Device (VID)
> >                      Name (_ADR, 0x00)  // _ADR: Address
> >                      Method (_DOS, 1, NotSerialized)  // _DOS: Disable Output Switching
> >                      Method (_DOD, 0, NotSerialized)  // _DOD: Display Output Devices
> >
> > PCI0.VID is a PCI device at pci0:0:2:0.
> > PCI0.PEG would be a PCI-PCI bridge at pci0:0:1:0.
> > It would have a child device at 0:0 that would be PCI0.PEG.VID.  Does the X220
> > have a switchable GPU (e.g. it has built-in Intel graphics, but also has an
> > Nvidia GPU or some such?).  If so, I imagine that PCI0.VID is the Intel graphics
> > and PEG is the non-Intel.  The output of 'pciconf -lcv' would be useful to determine
> > that.  If both PCI devices exist you shoudl have both acpi_video0 and acpi_video1.
> > However, it may be that the acpi_video driver doesn't cope well with having multiple
> > devices.
> Only Intel graphics, there is no option for switchable graphics.
> I initially thought that PEG was for Optimus usage, and left in the bios 
> by accident (i.e. Lenovo using a generic DSDT for many machines)
> 
> Here is pciconf -lcf, truncated
> hostb0 at pci0:0:0:0:    class=0x060000 card=0x21da17aa chip=0x01048086 
> rev=0x09 hdr=0x00
>      vendor     = 'Intel Corporation'
>      device     = '2nd Generation Core Processor Family DRAM Controller'
>      class      = bridge
>      subclass   = HOST-PCI
>      cap 09[e0] = vendor (length 12) Intel cap 0 version 1
> vgapci0 at pci0:0:2:0:    class=0x030000 card=0x21da17aa chip=0x01268086 
> rev=0x09 hdr=0x00
>      vendor     = 'Intel Corporation'
>      device     = '2nd Generation Core Processor Family Integrated 
> Graphics Controller'
>      class      = display
>      subclass   = VGA
>      cap 05[90] = MSI supports 1 message enabled with 1 message
>      cap 01[d0] = powerspec 2  supports D0 D3  current D0
>      cap 13[a4] = PCI Advanced Features: FLR TP
> none0 at pci0:0:22:0:    class=0x078000 card=0x21da17aa chip=0x1c3a8086 
> rev=0x04 hdr=0x00
>      vendor     = 'Intel Corporation'
> 
> As you can see there is no device at pci0:0:1:0. So no dev_t with for 
> acpi_video to probe or attach to.
> 
> Nonetheless, only PEGs ACPI methods work, which is quite broken. This is 
> true for a large number of Lenovo devices, back to x61 (non-attaching 
> AGP adr) and probably including some other x series and t series.
> 
> Unfortunately the ASL will not compile which makes fixing the DSDT an 
> exercise in fixing broken ACPI.
> 
> What I find interesting is that as far as I can tell, there's no special 
> case handling for this device in Linux, yet backlight controls work out 
> of the box since about 3.0. Installing Linux as the OSI via loader.conf 
> is not the issue, unfortunately, nor Windows 2006 (/WVIS) or Windows 
> 2009 (/WIN7). I get correct (for platform) behavior when I call PEGs 
> _BCM... :(
> 
> Is Linux getting this to work by doing it wrong, essentially?

Yes.  I think the best way to fix this is to add a way to specify a
hint to override the ACPI path associated with a PCI device.  Something
like:

hw.pci0.0.2.0.handle="\_SB_.PCI0.PEG.VID"

I think this patch should do the trick:

Index: sys/dev/acpica/acpi_pci.c
===================================================================
--- acpi_pci.c	(revision 247320)
+++ acpi_pci.c	(working copy)
@@ -264,6 +264,40 @@ acpi_pci_save_handle(ACPI_HANDLE handle, UINT32 le
 	return_ACPI_STATUS (AE_OK);
 }
 
+static void
+acpi_pci_override_handles(device_t dev)
+{
+	struct acpi_pci_devinfo *dinfo;
+	device_t *devlist;
+	int error, i, numdevs;
+	char tunable_name[64], *path;
+	ACPI_HANDLE handle;
+
+	error = device_get_children(dev, &devlist, &numdevs);
+	if (error)
+		return;
+	for (i = 0; i < numdevs; i++) {
+		dinfo = device_get_ivars(devlist[i]);
+		snprintf(tunable_name, sizeof(tunable_name),
+		    "hw.pci%d.%d.%d.%d.handle", dinfo->ap_dinfo.cfg.domain,
+		    dinfo->ap_dinfo.cfg.bus, dinfo->ap_dinfo.cfg.slot,
+		    dinfo->ap_dinfo.cfg.func);
+		path = getenv(tunable_name);
+		if (path == NULL)
+			continue;
+		if (ACPI_SUCCESS(AcpiGetHandle(NULL, path, &handle))) {
+			device_printf(dev,
+			    "Forcing device at %d.%d to use path %s\n",
+			    dinfo->ap_dinfo.cfg.slot,
+			    dinfo->ap_dinfo.cfg.func, path);
+			dinfo->ap_handle = handle;
+			acpi_pci_update_device(handle, devlist[i]);
+		}
+		freeenv(path);
+	}
+	free(devlist, M_TEMP);
+}
+
 static int
 acpi_pci_probe(device_t dev)
 {
@@ -306,5 +340,10 @@ acpi_pci_attach(device_t dev)
 	AcpiWalkNamespace(ACPI_TYPE_DEVICE, acpi_get_handle(dev), 1,
 	    acpi_pci_save_handle, NULL, dev, NULL);
 
+	/*
+	 * Perform another pass over child devices to allow their
+	 * handles to be overridden via a hint from the user.
+	 */
+	acpi_pci_override_handles(dev);
 	return (bus_generic_attach(dev));
 }


-- 
John Baldwin


More information about the freebsd-current mailing list