Add backlight support for WMI based Eee PC laptops. Signed-off-by: Yong Wang <yong.y.wang@xxxxxxxxx> --- drivers/platform/x86/eeepc-wmi.c | 170 +++++++++++++++++++++++++++++++++++++- 1 files changed, 167 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 0f5ab6a..784687c 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -30,6 +30,8 @@ #include <linux/slab.h> #include <linux/input.h> #include <linux/input/sparse-keymap.h> +#include <linux/fb.h> +#include <linux/backlight.h> #include <linux/platform_device.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> @@ -39,14 +41,21 @@ MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver"); MODULE_LICENSE("GPL"); #define EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000" +#define EEEPC_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" MODULE_ALIAS("wmi:"EEEPC_WMI_EVENT_GUID); +MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); #define NOTIFY_BRNUP_MIN 0x11 #define NOTIFY_BRNUP_MAX 0x1f #define NOTIFY_BRNDOWN_MIN 0x20 #define NOTIFY_BRNDOWN_MAX 0x2e +#define EEEPC_WMI_METHODID_DEVS 0x53564544 +#define EEEPC_WMI_METHODID_DSTS 0x53544344 + +#define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 + static const struct key_entry eeepc_wmi_keymap[] = { /* Sleep already handled via generic ACPI code */ { KE_KEY, 0x5d, { KEY_WLAN } }, @@ -59,9 +68,15 @@ static const struct key_entry eeepc_wmi_keymap[] = { { KE_END, 0}, }; +struct bios_args { + u32 dev_id; + u32 ctrl_param; +}; + struct eeepc_wmi { struct platform_device *platform_dev; struct input_dev *input_dev; + struct backlight_device *backlight_dev; }; static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc_wmi) @@ -104,6 +119,139 @@ static void eeepc_wmi_input_exit(struct eeepc_wmi *eeepc_wmi) eeepc_wmi->input_dev = NULL; } +static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *ctrl_param) +{ + struct acpi_buffer input = { (acpi_size)sizeof(u32), &dev_id }; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + u32 tmp; + + status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, + 1, EEEPC_WMI_METHODID_DSTS, &input, &output); + + if (ACPI_FAILURE(status)) + return status; + + obj = (union acpi_object *)output.pointer; + if (obj && obj->type == ACPI_TYPE_INTEGER) + tmp = (u32)obj->integer.value; + else + tmp = 0; + + if (ctrl_param) + *ctrl_param = tmp; + + kfree(obj); + + return status; + +} + +static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param) +{ + struct bios_args args = { + .dev_id = dev_id, + .ctrl_param = ctrl_param, + }; + struct acpi_buffer input = { (acpi_size)sizeof(args), &args }; + acpi_status status; + + status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, + 1, EEEPC_WMI_METHODID_DEVS, &input, NULL); + + return status; +} + +static int read_brightness(struct backlight_device *bd) +{ + static u32 ctrl_param; + acpi_status status; + + status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &ctrl_param); + + if (ACPI_FAILURE(status)) + return -1; + else + return ctrl_param & 0x000000FF; +} + +static int update_bl_status(struct backlight_device *bd) +{ + + static u32 ctrl_param; + acpi_status status; + + ctrl_param = bd->props.brightness; + + status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT, ctrl_param); + + if (ACPI_FAILURE(status)) + return -1; + else + return 0; +} + +static const struct backlight_ops eeepc_wmi_bl_ops = { + .get_brightness = read_brightness, + .update_status = update_bl_status, +}; + +static int eeepc_wmi_backlight_notify(struct eeepc_wmi *eeepc_wmi, bool increase) +{ + struct backlight_device *bd = eeepc_wmi->backlight_dev; + int old = bd->props.brightness; + int new; + + if (increase) + new = old + 1; + else + new = old - 1; + + if (new > bd->props.max_brightness) + new = bd->props.max_brightness; + else if (new < 0) + new = 0; + + bd->props.brightness = new; + backlight_update_status(bd); + backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY); + + return old; +} + +static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc_wmi) +{ + struct backlight_device *bd; + struct backlight_properties props; + + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = 15; + bd = backlight_device_register("eeepc-wmi", + &eeepc_wmi->platform_dev->dev, eeepc_wmi, + &eeepc_wmi_bl_ops, &props); + if (IS_ERR(bd)) { + pr_err("EEEPC WMI:Could not register backlight device\n"); + return PTR_ERR(bd); + } + + eeepc_wmi->backlight_dev = bd; + + bd->props.brightness = read_brightness(bd); + bd->props.power = FB_BLANK_UNBLANK; + backlight_update_status(bd); + + return 0; +} + +static void eeepc_wmi_backlight_exit(struct eeepc_wmi *eeepc_wmi) +{ + if (eeepc_wmi->backlight_dev) + backlight_device_unregister(eeepc_wmi->backlight_dev); + + eeepc_wmi->backlight_dev = NULL; +} + static void eeepc_wmi_notify(u32 value, void *context) { struct eeepc_wmi *eeepc_wmi = (struct eeepc_wmi *)context; @@ -128,6 +276,12 @@ static void eeepc_wmi_notify(u32 value, void *context) else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX) code = NOTIFY_BRNDOWN_MIN; + if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) { + if (!acpi_video_backlight_support()) + eeepc_wmi_backlight_notify(eeepc_wmi, + code == NOTIFY_BRNUP_MIN); + } + if (!sparse_keymap_report_event(eeepc_wmi->input_dev, code, 1, true)) pr_info("EEEPC WMI: Unknown key %x pressed\n", code); @@ -146,7 +300,14 @@ static int __devinit eeepc_wmi_platform_probe(struct platform_device *device) err = eeepc_wmi_input_init(eeepc_wmi); if (err) - return err; + goto error_input; + + if (!acpi_video_backlight_support()) { + err = eeepc_wmi_backlight_init(eeepc_wmi); + if (err) + goto error_backlight; + } else + pr_info("EEEPC WMI: Backlight controlled by ACPI video driver\n"); status = wmi_install_notify_handler(EEEPC_WMI_EVENT_GUID, eeepc_wmi_notify, eeepc_wmi); @@ -160,8 +321,10 @@ static int __devinit eeepc_wmi_platform_probe(struct platform_device *device) return 0; error_wmi: + eeepc_wmi_backlight_exit(eeepc_wmi); +error_backlight: eeepc_wmi_input_exit(eeepc_wmi); - +error_input: return err; } @@ -171,6 +334,7 @@ static int __devexit eeepc_wmi_platform_remove(struct platform_device *device) eeepc_wmi = wmi_get_driver_data(EEEPC_WMI_EVENT_GUID); wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID); + eeepc_wmi_backlight_exit(eeepc_wmi); eeepc_wmi_input_exit(eeepc_wmi); return 0; @@ -190,7 +354,7 @@ static int __init eeepc_wmi_init(void) struct eeepc_wmi *eeepc_wmi; int err; - if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID)) { + if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) || !wmi_has_guid(EEEPC_WMI_MGMT_GUID)) { pr_warning("EEEPC WMI: No known WMI GUID found\n"); return -ENODEV; } -- 1.5.5.1 -- To unsubscribe from this list: send the line "unsubscribe platform-driver-x86" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html