Browse Source

Merge branches 'acpi-osi', 'acpi-scan', 'acpi-tad', 'acpi-video' and 'acpi-misc'

* acpi-osi:
  ACPI / OSI: Add OEM _OSI strings to disable NVidia RTD3

* acpi-scan:
  ACPI / scan: Send change uevent with offine environmental data

* acpi-tad:
  ACPI: Add Time and Alarm Device (TAD) driver

* acpi-video:
  ACPI / video: Add quirk to force acpi-video backlight on Samsung 670Z5E

* acpi-misc:
  ACPI / Kconfig: Update ACPI_PROCFS_POWER help text
Rafael J. Wysocki 7 years ago

+ 113 - 0
Documentation/ABI/testing/sysfs-devices-platform-ACPI-TAD

@@ -0,0 +1,113 @@
+		ACPI Time and Alarm (TAD) device attributes.
+
+What:		/sys/bus/platform/devices/ACPI000E:00/caps
+Date:		March 2018
+Contact:	Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Description:
+		(RO) Hexadecimal bitmask of the TAD attributes are reported by
+		the platform firmware (see ACPI 6.2, section 9.18.2):
+
+		BIT(0): AC wakeup implemented if set
+		BIT(1): DC wakeup implemented if set
+		BIT(2): Get/set real time features implemented if set
+		BIT(3): Real time accuracy in milliseconds if set
+		BIT(4): Correct status reported for wakeups from S4/S5 if set
+		BIT(5): The AC timer wakes up from S4 if set
+		BIT(6): The AC timer wakes up from S5 if set
+		BIT(7): The DC timer wakes up from S4 if set
+		BIT(8): The DC timer wakes up from S5 if set
+
+		The other bits are reserved.
+
+What:		/sys/bus/platform/devices/ACPI000E:00/ac_alarm
+Date:		March 2018
+Contact:	Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Description:
+		(RW) The AC alarm timer value.
+
+		Reads return the current AC alarm timer value in seconds or
+		"disabled", if the AC alarm is not set to wake up the system.
+
+		Write a new AC alarm timer value in seconds or "disabled" to it
+		to set the AC alarm timer or to disable it, respectively.
+
+		If the AC alarm timer is set through this attribute and it
+		expires, it will immediately wake up the system from the S3
+		sleep state (and from S4/S5 too if supported) until its status
+		is explicitly cleared via the ac_status attribute.
+
+What:		/sys/bus/platform/devices/ACPI000E:00/ac_policy
+Date:		March 2018
+Contact:	Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Description:
+		(RW) The AC alarm expired timer wake policy (see ACPI 6.2,
+		Section 9.18 for details).
+
+		Reads return the current expired timer wake delay for the AC
+		alarm timer or "never", if the policy is to discard AC timer
+		wakeups if the system is on DC power.
+
+		Write a new expired timer wake delay for the AC alarm timer in
+		seconds or "never" to it to set the expired timer wake delay for
+		the AC alarm timer or to set its expired wake policy to discard
+		wakeups if the system is on DC power, respectively.
+
+What:		/sys/bus/platform/devices/ACPI000E:00/ac_status
+Date:		March 2018
+Contact:	Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Description:
+		(RW) The AC alarm status.
+
+		Reads return a hexadecimal bitmask representing the AC alarm
+		timer status with the following meaning of bits (see ACPI 6.2,
+		Section 9.18.5):
+
+		Bit(0): The timer has expired if set.
+		Bit(1): The timer has woken up the system from a sleep state
+		        (S3 or S4/S5 if supported) if set.
+
+		The other bits are reserved.
+
+		Reads also cause the AC alarm timer status to be reset.
+
+		Another way to reset the the status of the AC alarm timer is to
+		write (the number) 0 to this file.
+
+		If the status return value indicates that the timer has expired,
+		it will immediately wake up the system from the S3 sleep state
+		(and from S4/S5 too if supported) until its status is explicitly
+		cleared through this attribute.
+
+What:		/sys/bus/platform/devices/ACPI000E:00/dc_alarm
+Date:		March 2018
+Contact:	Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Description:
+		(RW,optional) The DC alarm timer value.
+
+		This attribute is only present if the TAD supports a separate
+		DC timer.
+
+		It is analogous to the ac_alarm attribute.
+
+What:		/sys/bus/platform/devices/ACPI000E:00/dc_policy
+Date:		March 2018
+Contact:	Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Description:
+		(RW,optional) The DC alarm expired timer wake policy.
+
+		This attribute is only present if the TAD supports a separate
+		DC timer.
+
+		It is analogous to the ac_policy attribute.
+
+What:		/sys/bus/platform/devices/ACPI000E:00/dc_status
+Date:		March 2018
+Contact:	Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+
+Description:
+		(RW,optional) The DC alarm status.
+
+		This attribute is only present if the TAD supports a separate
+		DC timer.
+
+		It is analogous to the ac_status attribute.

+ 17 - 4
drivers/acpi/Kconfig

@@ -105,14 +105,14 @@ config ACPI_PROCFS_POWER
           deprecated power /proc/acpi/ directories to exist, even when
           they have been replaced by functions in /sys.
           The deprecated directories (and their replacements) include:
-	  /proc/acpi/battery/* (/sys/class/power_supply/*)
-	  /proc/acpi/ac_adapter/* (sys/class/power_supply/*)
+	  /proc/acpi/battery/* (/sys/class/power_supply/*) and
+	  /proc/acpi/ac_adapter/* (sys/class/power_supply/*).
 	  This option has no effect on /proc/acpi/ directories
-	  and functions, which do not yet exist in /sys
+	  and functions which do not yet exist in /sys.
 	  This option, together with the proc directories, will be
 	  deleted in the future.
 
-	  Say N to delete power /proc/acpi/ directories that have moved to /sys/
+	  Say N to delete power /proc/acpi/ directories that have moved to /sys.
 
 config ACPI_REV_OVERRIDE_POSSIBLE
 	bool "Allow supported ACPI revision to be overridden"
@@ -217,6 +217,19 @@ config ACPI_FAN
 	  To compile this driver as a module, choose M here:
 	  the module will be called fan.
 
+config ACPI_TAD
+	tristate "ACPI Time and Alarm (TAD) Device Support"
+	depends on SYSFS && PM_SLEEP
+	help
+	  The ACPI Time and Alarm (TAD) device is an alternative to the Real
+	  Time Clock (RTC).  Its wake timers allow the system to transition from
+	  the S3 (or optionally S4/S5) state to S0 state after a time period
+	  elapses.  In comparison with the RTC Alarm, the TAD provides a larger
+	  scale of flexibility in the wake timers.  The time capabilities of the
+	  TAD maintain the time of day information across platform power
+	  transitions, and keep track of time even when the platform is turned
+	  off.
+
 config ACPI_DOCK
 	bool "Dock"
 	help

+ 1 - 0
drivers/acpi/Makefile

@@ -70,6 +70,7 @@ obj-$(CONFIG_ACPI_AC) 		+= ac.o
 obj-$(CONFIG_ACPI_BUTTON)	+= button.o
 obj-$(CONFIG_ACPI_FAN)		+= fan.o
 obj-$(CONFIG_ACPI_VIDEO)	+= video.o
+obj-$(CONFIG_ACPI_TAD)		+= acpi_tad.o
 obj-$(CONFIG_ACPI_PCI_SLOT)	+= pci_slot.o
 obj-$(CONFIG_ACPI_PROCESSOR)	+= processor.o
 obj-$(CONFIG_ACPI)		+= container.o

+ 473 - 0
drivers/acpi/acpi_tad.c

@@ -0,0 +1,473 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ACPI Time and Alarm (TAD) Device Driver
+ *
+ * Copyright (C) 2018 Intel Corporation
+ * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ *
+ * This driver is based on Section 9.18 of the ACPI 6.2 specification revision.
+ *
+ * It only supports the system wakeup capabilities of the TAD.
+ *
+ * Provided are sysfs attributes, available under the TAD platform device,
+ * allowing user space to manage the AC and DC wakeup timers of the TAD:
+ * set and read their values, set and check their expire timer wake policies,
+ * check and clear their status and check the capabilities of the TAD reported
+ * by AML.  The DC timer attributes are only present if the TAD supports a
+ * separate DC alarm timer.
+ *
+ * The wakeup events handling and power management of the TAD is expected to
+ * be taken care of by the ACPI PM domain attached to its platform device.
+ */
+
+#include <linux/acpi.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/suspend.h>
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Rafael J. Wysocki");
+
+/* ACPI TAD capability flags (ACPI 6.2, Section 9.18.2) */
+#define ACPI_TAD_AC_WAKE	BIT(0)
+#define ACPI_TAD_DC_WAKE	BIT(1)
+#define ACPI_TAD_RT		BIT(2)
+#define ACPI_TAD_RT_IN_MS	BIT(3)
+#define ACPI_TAD_S4_S5__GWS	BIT(4)
+#define ACPI_TAD_AC_S4_WAKE	BIT(5)
+#define ACPI_TAD_AC_S5_WAKE	BIT(6)
+#define ACPI_TAD_DC_S4_WAKE	BIT(7)
+#define ACPI_TAD_DC_S5_WAKE	BIT(8)
+
+/* ACPI TAD alarm timer selection */
+#define ACPI_TAD_AC_TIMER	(u32)0
+#define ACPI_TAD_DC_TIMER	(u32)1
+
+/* Special value for disabled timer or expired timer wake policy. */
+#define ACPI_TAD_WAKE_DISABLED	(~(u32)0)
+
+struct acpi_tad_driver_data {
+	u32 capabilities;
+};
+
+static int acpi_tad_wake_set(struct device *dev, char *method, u32 timer_id,
+			     u32 value)
+{
+	acpi_handle handle = ACPI_HANDLE(dev);
+	union acpi_object args[] = {
+		{ .type = ACPI_TYPE_INTEGER, },
+		{ .type = ACPI_TYPE_INTEGER, },
+	};
+	struct acpi_object_list arg_list = {
+		.pointer = args,
+		.count = ARRAY_SIZE(args),
+	};
+	unsigned long long retval;
+	acpi_status status;
+
+	args[0].integer.value = timer_id;
+	args[1].integer.value = value;
+
+	pm_runtime_get_sync(dev);
+
+	status = acpi_evaluate_integer(handle, method, &arg_list, &retval);
+
+	pm_runtime_put_sync(dev);
+
+	if (ACPI_FAILURE(status) || retval)
+		return -EIO;
+
+	return 0;
+}
+
+static int acpi_tad_wake_write(struct device *dev, const char *buf, char *method,
+			       u32 timer_id, const char *specval)
+{
+	u32 value;
+
+	if (sysfs_streq(buf, specval)) {
+		value = ACPI_TAD_WAKE_DISABLED;
+	} else {
+		int ret = kstrtou32(buf, 0, &value);
+
+		if (ret)
+			return ret;
+
+		if (value == ACPI_TAD_WAKE_DISABLED)
+			return -EINVAL;
+	}
+
+	return acpi_tad_wake_set(dev, method, timer_id, value);
+}
+
+static ssize_t acpi_tad_wake_read(struct device *dev, char *buf, char *method,
+				  u32 timer_id, const char *specval)
+{
+	acpi_handle handle = ACPI_HANDLE(dev);
+	union acpi_object args[] = {
+		{ .type = ACPI_TYPE_INTEGER, },
+	};
+	struct acpi_object_list arg_list = {
+		.pointer = args,
+		.count = ARRAY_SIZE(args),
+	};
+	unsigned long long retval;
+	acpi_status status;
+
+	args[0].integer.value = timer_id;
+
+	pm_runtime_get_sync(dev);
+
+	status = acpi_evaluate_integer(handle, method, &arg_list, &retval);
+
+	pm_runtime_put_sync(dev);
+
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	if ((u32)retval == ACPI_TAD_WAKE_DISABLED)
+		return sprintf(buf, "%s\n", specval);
+
+	return sprintf(buf, "%u\n", (u32)retval);
+}
+
+static const char *alarm_specval = "disabled";
+
+static int acpi_tad_alarm_write(struct device *dev, const char *buf,
+				u32 timer_id)
+{
+	return acpi_tad_wake_write(dev, buf, "_STV", timer_id, alarm_specval);
+}
+
+static ssize_t acpi_tad_alarm_read(struct device *dev, char *buf, u32 timer_id)
+{
+	return acpi_tad_wake_read(dev, buf, "_TIV", timer_id, alarm_specval);
+}
+
+static const char *policy_specval = "never";
+
+static int acpi_tad_policy_write(struct device *dev, const char *buf,
+				 u32 timer_id)
+{
+	return acpi_tad_wake_write(dev, buf, "_STP", timer_id, policy_specval);
+}
+
+static ssize_t acpi_tad_policy_read(struct device *dev, char *buf, u32 timer_id)
+{
+	return acpi_tad_wake_read(dev, buf, "_TIP", timer_id, policy_specval);
+}
+
+static int acpi_tad_clear_status(struct device *dev, u32 timer_id)
+{
+	acpi_handle handle = ACPI_HANDLE(dev);
+	union acpi_object args[] = {
+		{ .type = ACPI_TYPE_INTEGER, },
+	};
+	struct acpi_object_list arg_list = {
+		.pointer = args,
+		.count = ARRAY_SIZE(args),
+	};
+	unsigned long long retval;
+	acpi_status status;
+
+	args[0].integer.value = timer_id;
+
+	pm_runtime_get_sync(dev);
+
+	status = acpi_evaluate_integer(handle, "_CWS", &arg_list, &retval);
+
+	pm_runtime_put_sync(dev);
+
+	if (ACPI_FAILURE(status) || retval)
+		return -EIO;
+
+	return 0;
+}
+
+static int acpi_tad_status_write(struct device *dev, const char *buf, u32 timer_id)
+{
+	int ret, value;
+
+	ret = kstrtoint(buf, 0, &value);
+	if (ret)
+		return ret;
+
+	if (value)
+		return -EINVAL;
+
+	return acpi_tad_clear_status(dev, timer_id);
+}
+
+static ssize_t acpi_tad_status_read(struct device *dev, char *buf, u32 timer_id)
+{
+	acpi_handle handle = ACPI_HANDLE(dev);
+	union acpi_object args[] = {
+		{ .type = ACPI_TYPE_INTEGER, },
+	};
+	struct acpi_object_list arg_list = {
+		.pointer = args,
+		.count = ARRAY_SIZE(args),
+	};
+	unsigned long long retval;
+	acpi_status status;
+
+	args[0].integer.value = timer_id;
+
+	pm_runtime_get_sync(dev);
+
+	status = acpi_evaluate_integer(handle, "_GWS", &arg_list, &retval);
+
+	pm_runtime_put_sync(dev);
+
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	return sprintf(buf, "0x%02X\n", (u32)retval);
+}
+
+static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	struct acpi_tad_driver_data *dd = dev_get_drvdata(dev);
+
+	return sprintf(buf, "0x%02X\n", dd->capabilities);
+}
+
+static DEVICE_ATTR_RO(caps);
+
+static ssize_t ac_alarm_store(struct device *dev, struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	int ret = acpi_tad_alarm_write(dev, buf, ACPI_TAD_AC_TIMER);
+
+	return ret ? ret : count;
+}
+
+static ssize_t ac_alarm_show(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	return acpi_tad_alarm_read(dev, buf, ACPI_TAD_AC_TIMER);
+}
+
+static DEVICE_ATTR(ac_alarm, S_IRUSR | S_IWUSR, ac_alarm_show, ac_alarm_store);
+
+static ssize_t ac_policy_store(struct device *dev, struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	int ret = acpi_tad_policy_write(dev, buf, ACPI_TAD_AC_TIMER);
+
+	return ret ? ret : count;
+}
+
+static ssize_t ac_policy_show(struct device *dev, struct device_attribute *attr,
+			      char *buf)
+{
+	return acpi_tad_policy_read(dev, buf, ACPI_TAD_AC_TIMER);
+}
+
+static DEVICE_ATTR(ac_policy, S_IRUSR | S_IWUSR, ac_policy_show, ac_policy_store);
+
+static ssize_t ac_status_store(struct device *dev, struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	int ret = acpi_tad_status_write(dev, buf, ACPI_TAD_AC_TIMER);
+
+	return ret ? ret : count;
+}
+
+static ssize_t ac_status_show(struct device *dev, struct device_attribute *attr,
+			      char *buf)
+{
+	return acpi_tad_status_read(dev, buf, ACPI_TAD_AC_TIMER);
+}
+
+static DEVICE_ATTR(ac_status, S_IRUSR | S_IWUSR, ac_status_show, ac_status_store);
+
+static struct attribute *acpi_tad_attrs[] = {
+	&dev_attr_caps.attr,
+	&dev_attr_ac_alarm.attr,
+	&dev_attr_ac_policy.attr,
+	&dev_attr_ac_status.attr,
+	NULL,
+};
+static const struct attribute_group acpi_tad_attr_group = {
+	.attrs	= acpi_tad_attrs,
+};
+
+static ssize_t dc_alarm_store(struct device *dev, struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	int ret = acpi_tad_alarm_write(dev, buf, ACPI_TAD_DC_TIMER);
+
+	return ret ? ret : count;
+}
+
+static ssize_t dc_alarm_show(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	return acpi_tad_alarm_read(dev, buf, ACPI_TAD_DC_TIMER);
+}
+
+static DEVICE_ATTR(dc_alarm, S_IRUSR | S_IWUSR, dc_alarm_show, dc_alarm_store);
+
+static ssize_t dc_policy_store(struct device *dev, struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	int ret = acpi_tad_policy_write(dev, buf, ACPI_TAD_DC_TIMER);
+
+	return ret ? ret : count;
+}
+
+static ssize_t dc_policy_show(struct device *dev, struct device_attribute *attr,
+			      char *buf)
+{
+	return acpi_tad_policy_read(dev, buf, ACPI_TAD_DC_TIMER);
+}
+
+static DEVICE_ATTR(dc_policy, S_IRUSR | S_IWUSR, dc_policy_show, dc_policy_store);
+
+static ssize_t dc_status_store(struct device *dev, struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	int ret = acpi_tad_status_write(dev, buf, ACPI_TAD_DC_TIMER);
+
+	return ret ? ret : count;
+}
+
+static ssize_t dc_status_show(struct device *dev, struct device_attribute *attr,
+			      char *buf)
+{
+	return acpi_tad_status_read(dev, buf, ACPI_TAD_DC_TIMER);
+}
+
+static DEVICE_ATTR(dc_status, S_IRUSR | S_IWUSR, dc_status_show, dc_status_store);
+
+static struct attribute *acpi_tad_dc_attrs[] = {
+	&dev_attr_dc_alarm.attr,
+	&dev_attr_dc_policy.attr,
+	&dev_attr_dc_status.attr,
+	NULL,
+};
+static const struct attribute_group acpi_tad_dc_attr_group = {
+	.attrs	= acpi_tad_dc_attrs,
+};
+
+static int acpi_tad_disable_timer(struct device *dev, u32 timer_id)
+{
+	return acpi_tad_wake_set(dev, "_STV", timer_id, ACPI_TAD_WAKE_DISABLED);
+}
+
+static int acpi_tad_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct acpi_tad_driver_data *dd = dev_get_drvdata(dev);
+
+	device_init_wakeup(dev, false);
+
+	pm_runtime_get_sync(dev);
+
+	if (dd->capabilities & ACPI_TAD_DC_WAKE)
+		sysfs_remove_group(&dev->kobj, &acpi_tad_dc_attr_group);
+
+	sysfs_remove_group(&dev->kobj, &acpi_tad_attr_group);
+
+	acpi_tad_disable_timer(dev, ACPI_TAD_AC_TIMER);
+	acpi_tad_clear_status(dev, ACPI_TAD_AC_TIMER);
+	if (dd->capabilities & ACPI_TAD_DC_WAKE) {
+		acpi_tad_disable_timer(dev, ACPI_TAD_DC_TIMER);
+		acpi_tad_clear_status(dev, ACPI_TAD_DC_TIMER);
+	}
+
+	pm_runtime_put_sync(dev);
+	pm_runtime_disable(dev);
+	return 0;
+}
+
+static int acpi_tad_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	acpi_handle handle = ACPI_HANDLE(dev);
+	struct acpi_tad_driver_data *dd;
+	acpi_status status;
+	unsigned long long caps;
+	int ret;
+
+	/*
+	 * Initialization failure messages are mostly about firmware issues, so
+	 * print them at the "info" level.
+	 */
+	status = acpi_evaluate_integer(handle, "_GCP", NULL, &caps);
+	if (ACPI_FAILURE(status)) {
+		dev_info(dev, "Unable to get capabilities\n");
+		return -ENODEV;
+	}
+
+	if (!(caps & ACPI_TAD_AC_WAKE)) {
+		dev_info(dev, "Unsupported capabilities\n");
+		return -ENODEV;
+	}
+
+	if (!acpi_has_method(handle, "_PRW")) {
+		dev_info(dev, "Missing _PRW\n");
+		return -ENODEV;
+	}
+
+	dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);
+	if (!dd)
+		return -ENOMEM;
+
+	dd->capabilities = caps;
+	dev_set_drvdata(dev, dd);
+
+	/*
+	 * Assume that the ACPI PM domain has been attached to the device and
+	 * simply enable system wakeup and runtime PM and put the device into
+	 * runtime suspend.  Everything else should be taken care of by the ACPI
+	 * PM domain callbacks.
+	 */
+	device_init_wakeup(dev, true);
+	dev_pm_set_driver_flags(dev, DPM_FLAG_SMART_SUSPEND |
+				     DPM_FLAG_LEAVE_SUSPENDED);
+	/*
+	 * The platform bus type layer tells the ACPI PM domain powers up the
+	 * device, so set the runtime PM status of it to "active".
+	 */
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+	pm_runtime_suspend(dev);
+
+	ret = sysfs_create_group(&dev->kobj, &acpi_tad_attr_group);
+	if (ret)
+		goto fail;
+
+	if (caps & ACPI_TAD_DC_WAKE) {
+		ret = sysfs_create_group(&dev->kobj, &acpi_tad_dc_attr_group);
+		if (ret)
+			goto fail;
+	}
+
+	return 0;
+
+fail:
+	acpi_tad_remove(pdev);
+	return ret;
+}
+
+static const struct acpi_device_id acpi_tad_ids[] = {
+	{"ACPI000E", 0},
+	{}
+};
+
+static struct platform_driver acpi_tad_driver = {
+	.driver = {
+		.name = "acpi-tad",
+		.acpi_match_table = acpi_tad_ids,
+	},
+	.probe = acpi_tad_probe,
+	.remove = acpi_tad_remove,
+};
+MODULE_DEVICE_TABLE(acpi, acpi_tad_ids);
+
+module_platform_driver(acpi_tad_driver);

+ 9 - 0
drivers/acpi/osi.c

@@ -57,6 +57,15 @@ osi_setup_entries[OSI_STRING_ENTRIES_MAX] __initdata = {
 	{"Processor Device", true},
 	{"3.0 _SCP Extensions", true},
 	{"Processor Aggregator Device", true},
+	/*
+	 * Linux-Dell-Video is used by BIOS to disable RTD3 for NVidia graphics
+	 * cards as RTD3 is not supported by drivers now.  Systems with NVidia
+	 * cards will hang without RTD3 disabled.
+	 *
+	 * Once NVidia drivers officially support RTD3, this _OSI strings can
+	 * be removed if both new and old graphics cards are supported.
+	 */
+	{"Linux-Dell-Video", true},
 };
 
 static u32 acpi_osi_handler(acpi_string interface, u32 supported)

+ 2 - 1
drivers/acpi/scan.c

@@ -116,6 +116,7 @@ bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent)
 {
 	struct acpi_device_physical_node *pn;
 	bool offline = true;
+	char *envp[] = { "EVENT=offline", NULL };
 
 	/*
 	 * acpi_container_offline() calls this for all of the container's
@@ -126,7 +127,7 @@ bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent)
 	list_for_each_entry(pn, &adev->physical_node_list, node)
 		if (device_supports_offline(pn->dev) && !pn->dev->offline) {
 			if (uevent)
-				kobject_uevent(&pn->dev->kobj, KOBJ_CHANGE);
+				kobject_uevent_env(&pn->dev->kobj, KOBJ_CHANGE, envp);
 
 			offline = false;
 			break;

+ 9 - 0
drivers/acpi/video_detect.c

@@ -219,6 +219,15 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
 			  "3570R/370R/470R/450R/510R/4450RV"),
 		},
 	},
+	{
+	 /* https://bugzilla.redhat.com/show_bug.cgi?id=1557060 */
+	 .callback = video_detect_force_video,
+	 .ident = "SAMSUNG 670Z5E",
+	 .matches = {
+		DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+		DMI_MATCH(DMI_PRODUCT_NAME, "670Z5E"),
+		},
+	},
 	{
 	 /* https://bugzilla.redhat.com/show_bug.cgi?id=1094948 */
 	 .callback = video_detect_force_video,