I do a lot of support on IRC Freenode #linux and #ubuntu and we get a lot of this kind of report. We see a 90% success rate with a simple-to-apply workaround.

This is so common I wrote an article and fix [0] for it containing 4 shell commands that we refer users to whenever we get these 'works OK in Windows but not in Linux' type of reports.

By far the biggest cause of these hardware issues relating to power (suspend/resume, power-states, platform device enablement, ports - including GPU outputs) not working correctly is not caused by Linux but by the PC firmware itself and is down to the manufacturers.

In short - manufacturers customise the ACPI (Advanced Configuration and Power Interface) DSDT (Differentiated System Description Table) via it's _OSI() method to recognise only Microsoft Windows OSs and enable functionality based on the Windows version.

The version strings are of the form "Windows 2009" "Windows 2015" etc., where the later the year the more functionality is enabled. This is a total misuse of the purpose of _OSI but almost all manufacturers are doing this now.

As a result when Linux is the OS often only a minimal or incomplete set of functionality is enabled which causes all sorts of problems.

For a long time Linux has had code to report itself as various Windows versions to try to solve the worst of these but in many cases unless it claims to be the exact 'best' Windows version for each model/DSDT there will still be problems.

From silly things like some devices not working if the PC starts on battery (but fine starting on AC) - recently saw this was the cause of an external HDMI port not being enabled - through not handling power-state switching correctly and thus draining the battery rapidly, to suspend/resume/shutdown freezes and other symptoms.

The solution has frequently been to set the OSI string to match the most recent Windows version the PC's ACPI DSDT recognises, on the basis that the most recent is likely to enable the most functionality, using the DSDT OSI string via kernel command-line acpi_osi="...".

Checking the supported OSI version strings is quite easy from Linux:

sudo strings /sys/firmware/acpi/tables/DSDT | grep -i windows | sort

You'll get a sorted list with the most recent version generally at the end. E.g:

Microsoft Windows
Microsoft Windows NT
Microsoft WindowsME: Millennium Edition
Windows 2001
Windows 2006
Windows 2009
Windows 2012
Windows 2015

Adding the most recent version to the kernel command-line, either via the boot-menu at start-up to test it, but usually permanently via GRUB, in this form:

acpi_osi=! "acpi_osi=Windows XXXX"

where XXXX is the latest year reported. The initial =! tells Linux not to offer any of it's hard-coded version strings via OSI - this guarantees the one that gets used is the one specified.

On most GRUB-based systems this is done by editing /etc/default/grub and adding the options into the GRUB_CMDLINE_LINUX variable (there may be other kernel options there too) thus:

GRUB_CMDLINE_LINUX="acpi_osi=! \"acpi_osi=Windows XXXX\""

(note: the two \" are required to ensure the double-quote gets inserted onto the kernel command line surrounding the space in the version string)

then:

sudo update-grub

Reboot and test.

[0] http://iam.tj/prototype/enhancements/Windows-acpi_osi.html