All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/1] alienware-wmi: Update HDMI control methods
@ 2014-02-26 23:47 Mario Limonciello
  2014-02-26 23:47 ` [PATCH v3 1/1] Add WMI driver for controlling AlienFX and HDMI on Alienware Mario Limonciello
  2014-02-27  0:03 ` [PATCH v3 0/1] alienware-wmi: Update HDMI control methods Matthew Garrett
  0 siblings, 2 replies; 4+ messages in thread
From: Mario Limonciello @ 2014-02-26 23:47 UTC (permalink / raw)
  To: matthew.garrett; +Cc: platform-driver-x86, Mario Limonciello

Changes for v3:
 * Add HDMI control methods.  Will be restricted by DMI data because it 
   uses common GUID and WMI method as lighting control.  All platforms 
   with that GUID should support lighting control.
 * DMI data for HDMI supported platforms not yet locked.

Mario Limonciello (1):
  Add WMI driver for controlling AlienFX and HDMI on Alienware

 drivers/platform/x86/Kconfig         |   15 ++
 drivers/platform/x86/Makefile        |    1 +
 drivers/platform/x86/alienware-wmi.c |  372 ++++++++++++++++++++++++++++++++++
 3 files changed, 388 insertions(+)
 create mode 100644 drivers/platform/x86/alienware-wmi.c

-- 
1.7.9.5

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v3 1/1] Add WMI driver for controlling AlienFX and HDMI on Alienware
  2014-02-26 23:47 [PATCH v3 0/1] alienware-wmi: Update HDMI control methods Mario Limonciello
@ 2014-02-26 23:47 ` Mario Limonciello
  2014-02-27  0:03 ` [PATCH v3 0/1] alienware-wmi: Update HDMI control methods Matthew Garrett
  1 sibling, 0 replies; 4+ messages in thread
From: Mario Limonciello @ 2014-02-26 23:47 UTC (permalink / raw)
  To: matthew.garrett; +Cc: platform-driver-x86, Mario Limonciello

Specifically for platforms that don't contain an AlienFX USB based MCU
such as the Alienware X51 family.

Signed-off-by: Mario Limonciello <mario_limonciello@dell.com>
---
 drivers/platform/x86/Kconfig         |   15 ++
 drivers/platform/x86/Makefile        |    1 +
 drivers/platform/x86/alienware-wmi.c |  372 ++++++++++++++++++++++++++++++++++
 3 files changed, 388 insertions(+)
 create mode 100644 drivers/platform/x86/alienware-wmi.c

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 5ae65c1..640145b 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -55,6 +55,21 @@ config ACERHDF
 	  If you have an Acer Aspire One netbook, say Y or M
 	  here.
 
+config ALIENWARE_WMI
+	tristate "Alienware Special feature control"
+	depends on ACPI
+	depends on LEDS_CLASS
+	depends on NEW_LEDS
+	depends on ACPI_WMI
+	---help---
+	 This is a driver for controlling ALienware BIOS driven
+	 features.  It exposes an interface for controlling the AlienFX
+	 zones on Alienware machines that don't contain a dedicated AlienFX
+	 USB MCU such as the X51-R2.
+
+	 If you have an ACPI-WMI compatible Alienware desktop, say Y or M
+	 here.
+
 config ASUS_LAPTOP
 	tristate "Asus Laptop Extras"
 	depends on ACPI
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 9b87cfc..4fddf1a 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -56,3 +56,4 @@ obj-$(CONFIG_INTEL_SMARTCONNECT)	+= intel-smartconnect.o
 
 obj-$(CONFIG_PVPANIC)           += pvpanic.o
 obj-$(CONFIG_INTEL_BAYTRAIL_MBI)	+= intel_baytrail.o
+obj-$(CONFIG_ALIENWARE_WMI)	+= alienware-wmi.o
diff --git a/drivers/platform/x86/alienware-wmi.c b/drivers/platform/x86/alienware-wmi.c
new file mode 100644
index 0000000..e4cec49
--- /dev/null
+++ b/drivers/platform/x86/alienware-wmi.c
@@ -0,0 +1,372 @@
+/*
+ * Alienware AlienFX control
+ *
+ * Copyright (C) 2014 Dell Inc <mario_limonciello@dell.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/dmi.h>
+#include <linux/acpi.h>
+#include <linux/leds.h>
+
+MODULE_AUTHOR("Mario Limonciello <mario_limonciello@dell.com>");
+MODULE_DESCRIPTION("Alienware special feature control");
+MODULE_LICENSE("GPL");
+
+static struct platform_driver platform_driver = {
+	.driver = {
+		.name = "alienware-wmi",
+		.owner = THIS_MODULE,
+	}
+};
+
+static struct platform_device *platform_device;
+
+static const struct dmi_system_id hdmi_device_table[] __initconst = {
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "TBD"),
+		},
+	},
+	{}
+};
+
+MODULE_DEVICE_TABLE(dmi, hdmi_device_table);
+
+#define RUNNING_CONTROL_GUID		"A90597CE-A997-11DA-B012-B622A1EF5492"
+#define POWERSTATE_CONTROL_GUID		"A80593CE-A997-11DA-B012-B622A1EF5492"
+
+MODULE_ALIAS("wmi:" RUNNING_CONTROL_GUID);
+
+/*
+  Lighting Zone control groups
+*/
+
+#define ALIENWARE_HEAD_ZONE	1
+#define ALIENWARE_LEFT_ZONE	2
+#define ALIENWARE_RIGHT_ZONE	3
+
+enum LIGHTING_CONTROL_STATE {
+	RUNNING = 1,
+	BOOTING = 0,
+	SUSPEND = 3,
+};
+
+struct color_platform {
+	u8 blue;
+	u8 green;
+	u8 red;
+	u8 brightness;
+} __packed;
+
+struct platform_zone {
+	struct color_platform colors;
+	u8 location;
+};
+
+static struct platform_zone head = {
+	.location = ALIENWARE_HEAD_ZONE,
+};
+
+static struct platform_zone left = {
+	.location = ALIENWARE_LEFT_ZONE,
+};
+
+static struct platform_zone right = {
+	.location = ALIENWARE_RIGHT_ZONE,
+};
+
+static void update_leds(u8 lighting_state, struct platform_zone zone)
+{
+	acpi_status status;
+	char *guid;
+	struct acpi_buffer input;
+	struct platform_zone args;
+	if (lighting_state == BOOTING || lighting_state == SUSPEND) {
+		guid = POWERSTATE_CONTROL_GUID;
+		args.colors = zone.colors;
+		args.location = lighting_state;
+		input.length = (acpi_size) sizeof(args);
+		input.pointer = &args;
+	} else {
+		guid = RUNNING_CONTROL_GUID;
+		input.length = (acpi_size) sizeof(zone.colors);
+		input.pointer = &zone.colors;
+	}
+	pr_debug("alienware-wmi: evaluate [ guid %s | zone %d ]\n",
+		guid, zone.location);
+
+	status = wmi_evaluate_method(guid, 1, zone.location, &input, NULL);
+	if (ACPI_FAILURE(status))
+		pr_err("alienware-wmi: zone set failure: %u\n", status);
+}
+
+#define ALIEN_CREATE_LED_DEVICE(_state, _zone, _color)			\
+	static void _state##_##_zone##_##_color##_set(			\
+	struct led_classdev *led_cdev, enum led_brightness value)	\
+	{								\
+		_zone.colors._color = value;				\
+		update_leds(_state, _zone);				\
+	}								\
+									\
+	static struct led_classdev _state##_##_zone##_##_color##_led = {\
+		.brightness_set = _state##_##_zone##_##_color##_set,	\
+		.name = __stringify(alienware_wmi::_state##_##_zone##_##_color),\
+	};								\
+
+
+ALIEN_CREATE_LED_DEVICE(RUNNING, head, blue);
+ALIEN_CREATE_LED_DEVICE(RUNNING, head, red);
+ALIEN_CREATE_LED_DEVICE(RUNNING, head, green);
+ALIEN_CREATE_LED_DEVICE(RUNNING, head, brightness);
+ALIEN_CREATE_LED_DEVICE(RUNNING, left, blue);
+ALIEN_CREATE_LED_DEVICE(RUNNING, left, red);
+ALIEN_CREATE_LED_DEVICE(RUNNING, left, green);
+ALIEN_CREATE_LED_DEVICE(RUNNING, left, brightness);
+ALIEN_CREATE_LED_DEVICE(RUNNING, right, blue);
+ALIEN_CREATE_LED_DEVICE(RUNNING, right, red);
+ALIEN_CREATE_LED_DEVICE(RUNNING, right, green);
+ALIEN_CREATE_LED_DEVICE(RUNNING, right, brightness);
+
+ALIEN_CREATE_LED_DEVICE(BOOTING, head, blue);
+ALIEN_CREATE_LED_DEVICE(BOOTING, head, red);
+ALIEN_CREATE_LED_DEVICE(BOOTING, head, green);
+ALIEN_CREATE_LED_DEVICE(BOOTING, head, brightness);
+ALIEN_CREATE_LED_DEVICE(BOOTING, left, blue);
+ALIEN_CREATE_LED_DEVICE(BOOTING, left, red);
+ALIEN_CREATE_LED_DEVICE(BOOTING, left, green);
+ALIEN_CREATE_LED_DEVICE(BOOTING, left, brightness);
+ALIEN_CREATE_LED_DEVICE(BOOTING, right, blue);
+ALIEN_CREATE_LED_DEVICE(BOOTING, right, red);
+ALIEN_CREATE_LED_DEVICE(BOOTING, right, green);
+ALIEN_CREATE_LED_DEVICE(BOOTING, right, brightness);
+
+ALIEN_CREATE_LED_DEVICE(SUSPEND, head, blue);
+ALIEN_CREATE_LED_DEVICE(SUSPEND, head, red);
+ALIEN_CREATE_LED_DEVICE(SUSPEND, head, green);
+ALIEN_CREATE_LED_DEVICE(SUSPEND, head, brightness);
+ALIEN_CREATE_LED_DEVICE(SUSPEND, left, blue);
+ALIEN_CREATE_LED_DEVICE(SUSPEND, left, red);
+ALIEN_CREATE_LED_DEVICE(SUSPEND, left, green);
+ALIEN_CREATE_LED_DEVICE(SUSPEND, left, brightness);
+ALIEN_CREATE_LED_DEVICE(SUSPEND, right, blue);
+ALIEN_CREATE_LED_DEVICE(SUSPEND, right, red);
+ALIEN_CREATE_LED_DEVICE(SUSPEND, right, green);
+ALIEN_CREATE_LED_DEVICE(SUSPEND, right, brightness);
+
+
+static int alienware_zone_init(struct device *dev)
+{
+	led_classdev_register(dev, &RUNNING_head_blue_led);
+	led_classdev_register(dev, &RUNNING_head_red_led);
+	led_classdev_register(dev, &RUNNING_head_green_led);
+	led_classdev_register(dev, &RUNNING_head_brightness_led);
+	led_classdev_register(dev, &RUNNING_left_blue_led);
+	led_classdev_register(dev, &RUNNING_left_red_led);
+	led_classdev_register(dev, &RUNNING_left_green_led);
+	led_classdev_register(dev, &RUNNING_left_brightness_led);
+	led_classdev_register(dev, &RUNNING_right_blue_led);
+	led_classdev_register(dev, &RUNNING_right_red_led);
+	led_classdev_register(dev, &RUNNING_right_green_led);
+	led_classdev_register(dev, &RUNNING_right_brightness_led);
+	led_classdev_register(dev, &BOOTING_head_blue_led);
+	led_classdev_register(dev, &BOOTING_head_red_led);
+	led_classdev_register(dev, &BOOTING_head_green_led);
+	led_classdev_register(dev, &BOOTING_head_brightness_led);
+	led_classdev_register(dev, &BOOTING_left_blue_led);
+	led_classdev_register(dev, &BOOTING_left_red_led);
+	led_classdev_register(dev, &BOOTING_left_green_led);
+	led_classdev_register(dev, &BOOTING_left_brightness_led);
+	led_classdev_register(dev, &BOOTING_right_blue_led);
+	led_classdev_register(dev, &BOOTING_right_red_led);
+	led_classdev_register(dev, &BOOTING_right_green_led);
+	led_classdev_register(dev, &BOOTING_right_brightness_led);
+	led_classdev_register(dev, &SUSPEND_head_blue_led);
+	led_classdev_register(dev, &SUSPEND_head_red_led);
+	led_classdev_register(dev, &SUSPEND_head_green_led);
+	led_classdev_register(dev, &SUSPEND_head_brightness_led);
+	led_classdev_register(dev, &SUSPEND_left_blue_led);
+	led_classdev_register(dev, &SUSPEND_left_red_led);
+	led_classdev_register(dev, &SUSPEND_left_green_led);
+	led_classdev_register(dev, &SUSPEND_left_brightness_led);
+	led_classdev_register(dev, &SUSPEND_right_blue_led);
+	led_classdev_register(dev, &SUSPEND_right_red_led);
+	led_classdev_register(dev, &SUSPEND_right_green_led);
+	led_classdev_register(dev, &SUSPEND_right_brightness_led);
+	return 0;
+}
+
+static void alienware_zone_exit(void)
+{
+	led_classdev_unregister(&RUNNING_head_blue_led);
+	led_classdev_unregister(&RUNNING_head_red_led);
+	led_classdev_unregister(&RUNNING_head_green_led);
+	led_classdev_unregister(&RUNNING_head_brightness_led);
+	led_classdev_unregister(&RUNNING_left_blue_led);
+	led_classdev_unregister(&RUNNING_left_red_led);
+	led_classdev_unregister(&RUNNING_left_green_led);
+	led_classdev_unregister(&RUNNING_left_brightness_led);
+	led_classdev_unregister(&RUNNING_right_blue_led);
+	led_classdev_unregister(&RUNNING_right_red_led);
+	led_classdev_unregister(&RUNNING_right_green_led);
+	led_classdev_unregister(&RUNNING_right_brightness_led);
+	led_classdev_unregister(&BOOTING_head_blue_led);
+	led_classdev_unregister(&BOOTING_head_red_led);
+	led_classdev_unregister(&BOOTING_head_green_led);
+	led_classdev_unregister(&BOOTING_head_brightness_led);
+	led_classdev_unregister(&BOOTING_left_blue_led);
+	led_classdev_unregister(&BOOTING_left_red_led);
+	led_classdev_unregister(&BOOTING_left_green_led);
+	led_classdev_unregister(&BOOTING_left_brightness_led);
+	led_classdev_unregister(&BOOTING_right_blue_led);
+	led_classdev_unregister(&BOOTING_right_red_led);
+	led_classdev_unregister(&BOOTING_right_green_led);
+	led_classdev_unregister(&BOOTING_right_brightness_led);
+	led_classdev_unregister(&SUSPEND_head_blue_led);
+	led_classdev_unregister(&SUSPEND_head_red_led);
+	led_classdev_unregister(&SUSPEND_head_green_led);
+	led_classdev_unregister(&SUSPEND_head_brightness_led);
+	led_classdev_unregister(&SUSPEND_left_blue_led);
+	led_classdev_unregister(&SUSPEND_left_red_led);
+	led_classdev_unregister(&SUSPEND_left_green_led);
+	led_classdev_unregister(&SUSPEND_left_brightness_led);
+	led_classdev_unregister(&SUSPEND_right_blue_led);
+	led_classdev_unregister(&SUSPEND_right_red_led);
+	led_classdev_unregister(&SUSPEND_right_green_led);
+	led_classdev_unregister(&SUSPEND_right_brightness_led);
+}
+
+/*
+  HDMI mux control
+*/
+
+static ssize_t show_hdmi(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	acpi_status status;
+	status = wmi_evaluate_method(RUNNING_CONTROL_GUID, 3, NULL, NULL, NULL);
+	if (status == 1) {
+		sprintf(buf, "hdmi-in\n");
+		return 0;
+	} else if (status == 2) {
+		sprintf(buf, "gpu\n");
+		return 0;
+	}
+	pr_err("alienware-wmi: HDMI mux read failed: results: %u\n", status);
+	return status;
+}
+
+static ssize_t toggle_hdmi(struct device *dev, struct device_attribute *attr,
+			   const char *buf, size_t count)
+{
+	acpi_status status;
+	status = wmi_evaluate_method(RUNNING_CONTROL_GUID, 2, 3, NULL, NULL);
+	if (ACPI_FAILURE(status))
+		pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
+			   status);
+	return count;
+}
+
+static DEVICE_ATTR(hdmi, S_IRUGO | S_IWUSR, show_hdmi, toggle_hdmi);
+
+static void remove_hdmi(struct platform_device *device)
+{
+	device_remove_file(&device->dev, &dev_attr_hdmi);
+}
+
+static int create_hdmi(void)
+{
+	int ret = -ENOMEM;
+
+	ret = device_create_file(&platform_device->dev, &dev_attr_hdmi);
+	if (ret)
+		goto error_create_hdmi;
+	return 0;
+
+error_create_hdmi:
+	remove_hdmi(platform_device);
+	return ret;
+}
+
+static int __init alienware_wmi_init(void)
+{
+	int ret;
+
+	if (!wmi_has_guid(RUNNING_CONTROL_GUID)) {
+		pr_warn("No known WMI GUID found\n");
+		return -ENODEV;
+	}
+
+	ret = platform_driver_register(&platform_driver);
+	if (ret)
+		goto fail_platform_driver;
+	platform_device = platform_device_alloc("alienware-wmi", -1);
+	if (!platform_device) {
+		ret = -ENOMEM;
+		goto fail_platform_device1;
+	}
+	ret = platform_device_add(platform_device);
+	if (ret)
+		goto fail_platform_device2;
+
+	/*
+		HDMI mux control is on the WMAW method too
+		but only on certain systems.
+	*/
+	if (dmi_check_system(hdmi_device_table)) {
+		ret = create_hdmi();
+		if (ret)
+			goto fail_prep_hdmi;
+	}
+	/*
+		Non-MCU driven lighting control
+	*/
+	if (wmi_has_guid(RUNNING_CONTROL_GUID)) {
+		ret = alienware_zone_init(&platform_device->dev);
+		if (ret)
+			goto fail_prep_zones;
+	}
+
+	return 0;
+
+fail_prep_zones:
+	alienware_zone_exit();
+fail_prep_hdmi:
+	platform_device_del(platform_device);
+fail_platform_device2:
+	platform_device_put(platform_device);
+fail_platform_device1:
+	platform_driver_unregister(&platform_driver);
+fail_platform_driver:
+	return ret;
+}
+
+module_init(alienware_wmi_init);
+
+static void __exit alienware_wmi_exit(void)
+{
+	alienware_zone_exit();
+	remove_hdmi(platform_device);
+	if (platform_device) {
+		platform_device_unregister(platform_device);
+		platform_driver_unregister(&platform_driver);
+	}
+}
+
+module_exit(alienware_wmi_exit);
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH v3 0/1] alienware-wmi: Update HDMI control methods
  2014-02-26 23:47 [PATCH v3 0/1] alienware-wmi: Update HDMI control methods Mario Limonciello
  2014-02-26 23:47 ` [PATCH v3 1/1] Add WMI driver for controlling AlienFX and HDMI on Alienware Mario Limonciello
@ 2014-02-27  0:03 ` Matthew Garrett
  2014-02-27  0:16   ` Mario Limonciello
  1 sibling, 1 reply; 4+ messages in thread
From: Matthew Garrett @ 2014-02-27  0:03 UTC (permalink / raw)
  To: mario_limonciello; +Cc: platform-driver-x86

On Wed, 2014-02-26 at 17:47 -0600, Mario Limonciello wrote:
> Changes for v3:
>  * Add HDMI control methods.  Will be restricted by DMI data because it 
>    uses common GUID and WMI method as lighting control.  All platforms 
>    with that GUID should support lighting control.
>  * DMI data for HDMI supported platforms not yet locked.

Could you clarify what the HDMI mux is for? Is there any method in the
HDMI WMI call to determine whether it's supported rather than relying on
DMI? (I appreciate that in this kind of case there may not be)

-- 
Matthew Garrett <matthew.garrett@nebula.com>

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v3 0/1] alienware-wmi: Update HDMI control methods
  2014-02-27  0:03 ` [PATCH v3 0/1] alienware-wmi: Update HDMI control methods Matthew Garrett
@ 2014-02-27  0:16   ` Mario Limonciello
  0 siblings, 0 replies; 4+ messages in thread
From: Mario Limonciello @ 2014-02-27  0:16 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: platform-driver-x86

On 02/26/2014 06:03 PM, Matthew Garrett wrote
> Could you clarify what the HDMI mux is for? Is there any method in the
> HDMI WMI call to determine whether it's supported rather than relying on
> DMI? (I appreciate that in this kind of case there may not be)
>
The HDMI mux will control what's displayed on the HDMI output port. It can toggle between an HDMI input device and the system's standard video output.

Since the same GUID and WMI method are used both on platforms that support HDMI mux as well as platforms that support just lighting control I don't think it will be possible to determine if it's supported.  You could possibly make a WMI call and attempting to toggle or query, however the behavior is undefined for those arguments on firmware for other systems.  While I was initially putting this driver together I made mistakes with the arguments early on that caused very colorful corruption all over the display, so I think it would be better to restrict it by DMI data if possible.

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2014-02-27  0:16 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-02-26 23:47 [PATCH v3 0/1] alienware-wmi: Update HDMI control methods Mario Limonciello
2014-02-26 23:47 ` [PATCH v3 1/1] Add WMI driver for controlling AlienFX and HDMI on Alienware Mario Limonciello
2014-02-27  0:03 ` [PATCH v3 0/1] alienware-wmi: Update HDMI control methods Matthew Garrett
2014-02-27  0:16   ` Mario Limonciello

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.