Re: [PATCH] ACPI: Support Fujitsu Lifebook S6410 in fujitsu-laptop

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



An updated version of the support for Fujitsu Lifebook S6410 in
fujitsu-laptop. Fixes adding of the notification and the brightness
button if no brightness change occured.

To activate the different brightness-button handling use the 
use_alt_lcd_levels=1 module parameter.

Signed-off-by: Peter Gruber <nokos@xxxxxxx>

> This adds Support for Fujitsu Lifebook S6410 in fujitsu-laptop. The
> module parameter use_alt_lcd_levels switches between different ACPI
> brightness controls (The one in the original does not work for the
> S6410, it only set the appropriate ACPI registerbut does not change the
> brightness). With use_alt_lcd_levels=1 it has been testen on a Fujitsu
> Lifebook S6410. Here it also provides an input device for the extra
> buttons found on that laptop.
> 
> The entry in blacklist.c is needed since the BIOS contains an ACPI Table
> which has no signature (ie. "\0\0\0\0" ) which contains the brightness
> adjustment functions if "Windows 2006" is detected by osi. With this
> table the brightness buttons would be routed through the standard VIDEO
> device but brightness adjustments would also not work.


diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c
index ea92bac..8d02671 100644
--- a/drivers/acpi/blacklist.c
+++ b/drivers/acpi/blacklist.c
@@ -447,6 +447,14 @@ static struct dmi_system_id acpi_osi_dmi_table[]
__initdata = {
 		     DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
 	 	     DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile V5505"),
 		},
+	{
+	.callback = dmi_disable_osi_vista,
+	.ident = "Fujitsu Siemens",
+	.matches = {
+	             DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+	             DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6410"),
+	         },
+	},
 	},
 	/*
 	 * Disable OSI(Linux) warnings on all "Hewlett-Packard"
diff --git a/drivers/misc/fujitsu-laptop.c
b/drivers/misc/fujitsu-laptop.c
index 1cfd7f3..4c871ba 100644
--- a/drivers/misc/fujitsu-laptop.c
+++ b/drivers/misc/fujitsu-laptop.c
@@ -2,6 +2,7 @@
 
 /*
   Copyright (C) 2007 Jonathan Woithe <jwoithe@xxxxxxxxxxxxxxxxxxxxxxx>
+  Copyright (C) 2008 Peter Gruber <nokos@xxxxxxx>
   Based on earlier work:
     Copyright (C) 2003 Shane Spencer <shane@xxxxxxxxxxx>
     Adrian Yee <brewt-fujitsu@xxxxxxxxx>
@@ -41,6 +42,12 @@
  *
  * This driver has been tested on a Fujitsu Lifebook S7020.  It should
  * work on most P-series and S-series Lifebooks, but YMMV.
+ *
+ * The moduleparameter use_alt_lcd_levels switches between different
ACPI
+ * brightness controls. With use_alt_lcd_levels=1 it has been testen on
+ * a Fujitsu Lifebook S6410. Here it also provides an input device for
the
+ * extra buttons found on that laptop.
+ *
  */
 
 #include <linux/module.h>
@@ -49,31 +56,82 @@
 #include <linux/acpi.h>
 #include <linux/dmi.h>
 #include <linux/backlight.h>
+#include <linux/input.h>
+#include <linux/video_output.h>
 #include <linux/platform_device.h>
+#include <linux/autoconf.h>
 
 #define FUJITSU_DRIVER_VERSION "0.3"
 
 #define FUJITSU_LCD_N_LEVELS 8
 
 #define ACPI_FUJITSU_CLASS              "fujitsu"
-#define ACPI_FUJITSU_HID                "FUJ02B1"
-#define ACPI_FUJITSU_DRIVER_NAME        "Fujitsu laptop FUJ02B1 ACPI
extras driver"
-#define ACPI_FUJITSU_DEVICE_NAME        "Fujitsu FUJ02B1"
+
+#define ACPI_FUJITSUB1_DRIVER_NAME        "Fujitsu laptop ACPI extras
driver B1"
+#define ACPI_FUJITSUB1_HID                "FUJ02B1"
+#define ACPI_FUJITSUB1_DEVICE_NAME        "Fujitsu FUJ02B1"
+
+#define ACPI_FUJITSUE3_DRIVER_NAME        "Fujitsu laptop ACPI extras
driver E3"
+#define ACPI_FUJITSUE3_HID                "FUJ02E3"
+#define ACPI_FUJITSUE3_DEVICE_NAME        "Fujitsu FUJ02E3"
+
+#define ACPI_FUJITSUB1_NOTIFY_CODE1	0x80
+#define ACPI_FUJITSUE3_NOTIFY_CODE1	0x80
+
+#define	ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS	0x86
+#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS	0x87
+
+#define LOCK_KEY	0x410 /* codes for the keys in the GIRB register */
+#define DISPLAY_KEY	0x411 /* keys are mapped to KEY_SCREENLOCK (the key
with the key symbol) */
+#define ENERGY_KEY	0x412 /* KEY_MEDIA (the key with the laptop symbol,
KEY_EMAIL (E key)) */
+#define REST_KEY	0x413 /* KEY_SUSPEND (R key)*/
 
 struct fujitsu_t {
-	acpi_handle acpi_handle;
+	int usealt;		/* use the alternative brightness interface */
+	acpi_handle acpi_handle_b1;
+	acpi_handle acpi_handle_e3;
+	struct acpi_device *dev_b1;
+	struct acpi_device *dev_e3;
+	struct input_dev *input_b1;
+	struct input_dev *input_e3;
+	char phys_b1[32];
+	char phys_e3[32];
 	struct backlight_device *bl_device;
 	struct platform_device *pf_device;
+	int keycode_e3;		/* remember keycode for release */
 
-	unsigned long fuj02b1_state;
+	unsigned int max_brightness;
 	unsigned int brightness_changed;
 	unsigned int brightness_level;
+	unsigned int irb;	/* info about the pressed buttons */
 };
 
 static struct fujitsu_t *fujitsu;
+static int use_alt_lcd_levels;
+
+static void acpi_fujitsu_notify_b1(acpi_handle handle, u32 event,
+				   void *data);
+static void acpi_fujitsu_notify_e3(acpi_handle handle, u32 event,
+				   void *data);
 
 /* Hardware access */
 
+static int get_irb(void)
+{
+	unsigned long state = 0;
+	acpi_status status = AE_OK;
+
+	status =
+	    acpi_evaluate_integer(fujitsu->acpi_handle_e3, "GIRB", NULL,
+				  &state);
+	if (status < 0)
+		return status;
+
+	fujitsu->irb = state;
+
+	return fujitsu->irb;
+}
+
 static int set_lcd_level(int level)
 {
 	acpi_status status = AE_OK;
@@ -81,13 +139,13 @@ static int set_lcd_level(int level)
 	struct acpi_object_list arg_list = { 1, &arg0 };
 	acpi_handle handle = NULL;
 
-	if (level < 0 || level >= FUJITSU_LCD_N_LEVELS)
+	if (level < 0 || level >= fujitsu->max_brightness)
 		return -EINVAL;
 
 	if (!fujitsu)
 		return -EINVAL;
 
-	status = acpi_get_handle(fujitsu->acpi_handle, "SBLL", &handle);
+	status = acpi_get_handle(fujitsu->acpi_handle_b1, "SBLL", &handle);
 	if (ACPI_FAILURE(status)) {
 		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "SBLL not present\n"));
 		return -ENODEV;
@@ -102,18 +160,82 @@ static int set_lcd_level(int level)
 	return 0;
 }
 
+static int set_lcd_level_alt(int level)
+{
+	acpi_status status = AE_OK;
+	union acpi_object arg0 = { ACPI_TYPE_INTEGER };
+	struct acpi_object_list arg_list = { 1, &arg0 };
+	acpi_handle handle = NULL;
+
+	if (level < 0 || level >= fujitsu->max_brightness)
+		return -EINVAL;
+
+	if (!fujitsu)
+		return -EINVAL;
+
+	status = acpi_get_handle(fujitsu->acpi_handle_b1, "SBL2", &handle);
+	if (ACPI_FAILURE(status)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "SBL2 not present\n"));
+		return -ENODEV;
+	}
+
+	arg0.integer.value = level;
+
+	status = acpi_evaluate_object(handle, NULL, &arg_list, NULL);
+	if (ACPI_FAILURE(status))
+		return -ENODEV;
+
+	return 0;
+}
+
 static int get_lcd_level(void)
 {
 	unsigned long state = 0;
 	acpi_status status = AE_OK;
 
-	// Get the Brightness
 	status =
-	    acpi_evaluate_integer(fujitsu->acpi_handle, "GBLL", NULL, &state);
+	    acpi_evaluate_integer(fujitsu->acpi_handle_b1, "GBLL", NULL,
+				  &state);
+	if (status < 0)
+		return status;
+
+	fujitsu->brightness_level = state & 0x0fffffff;
+
+	if (state & 0x80000000)
+		fujitsu->brightness_changed = 1;
+	else
+		fujitsu->brightness_changed = 0;
+
+	return fujitsu->brightness_level;
+}
+
+static int get_max_brightness(void)
+{
+	unsigned long state = 0;
+	acpi_status status = AE_OK;
+
+	status =
+	    acpi_evaluate_integer(fujitsu->acpi_handle_b1, "RBLL", NULL,
+				  &state);
+	if (status < 0)
+		return status;
+
+	fujitsu->max_brightness = state;
+
+	return fujitsu->max_brightness;
+}
+
+static int get_lcd_level_alt(void)
+{
+	unsigned long state = 0;
+	acpi_status status = AE_OK;
+
+	status =
+	    acpi_evaluate_integer(fujitsu->acpi_handle_b1, "GBLS", NULL,
+				  &state);
 	if (status < 0)
 		return status;
 
-	fujitsu->fuj02b1_state = state;
 	fujitsu->brightness_level = state & 0x0fffffff;
 
 	if (state & 0x80000000)
@@ -124,8 +246,24 @@ static int get_lcd_level(void)
 	return fujitsu->brightness_level;
 }
 
+
 /* Backlight device stuff */
 
+static int bl_get_brightness_alt(struct backlight_device *b)
+{
+	return get_lcd_level_alt();
+}
+
+static int bl_update_status_alt(struct backlight_device *b)
+{
+	return set_lcd_level_alt(b->props.brightness);
+}
+
+static struct backlight_ops fujitsubl_ops_alt = {
+	.get_brightness = bl_get_brightness_alt,
+	.update_status = bl_update_status_alt,
+};
+
 static int bl_get_brightness(struct backlight_device *b)
 {
 	return get_lcd_level();
@@ -143,8 +281,56 @@ static struct backlight_ops fujitsubl_ops = {
 
 /* Platform device */
 
-static ssize_t show_lcd_level(struct device *dev,
-			      struct device_attribute *attr, char *buf)
+static ssize_t
+show_lcd_level_alt(struct device *dev,
+		   struct device_attribute *attr, char *buf)
+{
+
+	int ret;
+
+	ret = get_lcd_level_alt();
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%i\n", ret);
+}
+
+static ssize_t
+store_lcd_level_alt(struct device *dev,
+		    struct device_attribute *attr, const char *buf,
+		    size_t count)
+{
+
+	int level, ret;
+
+	if (sscanf(buf, "%i", &level) != 1
+	    || (level < 0 || level >= fujitsu->max_brightness))
+		return -EINVAL;
+
+	ret = set_lcd_level_alt(level);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t
+show_max_brightness(struct device *dev,
+		    struct device_attribute *attr, char *buf)
+{
+
+	int ret;
+
+	ret = get_max_brightness();
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%i\n", ret);
+}
+
+static ssize_t
+show_lcd_level(struct device *dev, struct device_attribute *attr,
+	       char *buf)
 {
 
 	int ret;
@@ -156,15 +342,16 @@ static ssize_t show_lcd_level(struct device *dev,
 	return sprintf(buf, "%i\n", ret);
 }
 
-static ssize_t store_lcd_level(struct device *dev,
-			       struct device_attribute *attr, const char *buf,
-			       size_t count)
+static ssize_t
+store_lcd_level(struct device *dev,
+		struct device_attribute *attr, const char *buf,
+		size_t count)
 {
 
 	int level, ret;
 
 	if (sscanf(buf, "%i", &level) != 1
-	    || (level < 0 || level >= FUJITSU_LCD_N_LEVELS))
+	    || (level < 0 || level >= fujitsu->max_brightness))
 		return -EINVAL;
 
 	ret = set_lcd_level(level);
@@ -174,10 +361,23 @@ static ssize_t store_lcd_level(struct device *dev,
 	return count;
 }
 
+static ssize_t
+ignore_store(struct device *dev,
+	     struct device_attribute *attr, const char *buf, size_t count)
+{
+	return count;
+}
+
+static DEVICE_ATTR(max_brightness, 0444, show_max_brightness,
+		   ignore_store);
 static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
+static DEVICE_ATTR(lcd_level_alt, 0644, show_lcd_level_alt,
+		   store_lcd_level_alt);
 
 static struct attribute *fujitsupf_attributes[] = {
+	&dev_attr_max_brightness.attr,
 	&dev_attr_lcd_level.attr,
+	&dev_attr_lcd_level_alt.attr,
 	NULL
 };
 
@@ -194,22 +394,61 @@ static struct platform_driver fujitsupf_driver = {
 
 /* ACPI device */
 
-static int acpi_fujitsu_add(struct acpi_device *device)
+static int acpi_fujitsu_add_b1(struct acpi_device *device)
 {
+	acpi_status status;
 	int result = 0;
 	int state = 0;
+	struct input_dev *input;
+	int error;
 
-	ACPI_FUNCTION_TRACE("acpi_fujitsu_add");
+	ACPI_FUNCTION_TRACE("acpi_fujitsu_add_b1");
 
 	if (!device)
 		return -EINVAL;
 
-	fujitsu->acpi_handle = device->handle;
-	sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_DEVICE_NAME);
+	fujitsu->acpi_handle_b1 = device->handle;
+	sprintf(acpi_device_name(device), "%s",
+		ACPI_FUJITSUB1_DEVICE_NAME);
 	sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
 	acpi_driver_data(device) = fujitsu;
 
-	result = acpi_bus_get_power(fujitsu->acpi_handle, &state);
+	status = acpi_install_notify_handler(device->handle,
+					     ACPI_DEVICE_NOTIFY,
+					     acpi_fujitsu_notify_b1,
+					     fujitsu);
+
+	if (ACPI_FAILURE(status)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Error installing notify handler\n"));
+		error = -ENODEV;
+		goto err_stop;
+	}
+
+	fujitsu->input_b1 = input = input_allocate_device();
+	if (!input) {
+		error = -ENOMEM;
+		goto err_uninstall_notify;
+	}
+
+	snprintf(fujitsu->phys_b1, sizeof(fujitsu->phys_b1),
+		 "%s/video/input0", acpi_device_hid(device));
+
+	input->name = acpi_device_name(device);
+	input->phys = fujitsu->phys_b1;
+	input->id.bustype = BUS_HOST;
+	input->id.product = 0x06;
+	input->dev.parent = &device->dev;
+	input->evbit[0] = BIT(EV_KEY);
+	set_bit(KEY_BRIGHTNESSUP, input->keybit);
+	set_bit(KEY_BRIGHTNESSDOWN, input->keybit);
+	set_bit(KEY_UNKNOWN, input->keybit);
+
+	error = input_register_device(input);
+	if (error)
+		goto err_free_input_dev;
+
+	result = acpi_bus_get_power(fujitsu->acpi_handle_b1, &state);
 	if (result) {
 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
 				  "Error reading power state\n"));
@@ -220,42 +459,311 @@ static int acpi_fujitsu_add(struct acpi_device
*device)
 	       acpi_device_name(device), acpi_device_bid(device),
 	       !device->power.state ? "on" : "off");
 
+	if (get_max_brightness() <= 0)
+		fujitsu->max_brightness = FUJITSU_LCD_N_LEVELS;
+
+	fujitsu->dev_b1 = device;
+
+	return result;
+
       end:
+      err_free_input_dev:
+	input_free_device(input);
+      err_uninstall_notify:
+	acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
+				   acpi_fujitsu_notify_b1);
+      err_stop:
 
 	return result;
 }
 
-static int acpi_fujitsu_remove(struct acpi_device *device, int type)
+static int acpi_fujitsu_remove_b1(struct acpi_device *device, int type)
 {
-	ACPI_FUNCTION_TRACE("acpi_fujitsu_remove");
+	acpi_status status;
+	struct fujitsu_t *fujitsu = NULL;
+
+	ACPI_FUNCTION_TRACE("acpi_fujitsu_remove_b1");
 
 	if (!device || !acpi_driver_data(device))
 		return -EINVAL;
-	fujitsu->acpi_handle = 0;
+
+	fujitsu = acpi_driver_data(device);
+
+	status = acpi_remove_notify_handler(fujitsu->acpi_handle_b1,
+					    ACPI_DEVICE_NOTIFY,
+					    acpi_fujitsu_notify_b1);
+
+	if (!device || !acpi_driver_data(device))
+		return -EINVAL;
+
+	fujitsu->acpi_handle_b1 = 0;
 
 	return 0;
 }
 
-static const struct acpi_device_id fujitsu_device_ids[] = {
-	{ACPI_FUJITSU_HID, 0},
+static const struct acpi_device_id fujitsu_device_ids_b1[] = {
+	{ACPI_FUJITSUB1_HID, 0},
 	{"", 0},
 };
 
-static struct acpi_driver acpi_fujitsu_driver = {
-	.name = ACPI_FUJITSU_DRIVER_NAME,
+static struct acpi_driver acpi_fujitsu_driver_b1 = {
+	.name = ACPI_FUJITSUB1_DRIVER_NAME,
 	.class = ACPI_FUJITSU_CLASS,
-	.ids = fujitsu_device_ids,
+	.ids = fujitsu_device_ids_b1,
 	.ops = {
-		.add = acpi_fujitsu_add,
-		.remove = acpi_fujitsu_remove,
+		.add = acpi_fujitsu_add_b1,
+		.remove = acpi_fujitsu_remove_b1,
+		},
+};
+
+static int acpi_fujitsu_add_e3(struct acpi_device *device)
+{
+	acpi_status status;
+	int result = 0;
+	int state = 0;
+	struct input_dev *input;
+	int error;
+
+	ACPI_FUNCTION_TRACE("acpi_fujitsu_add_e3");
+
+	if (!device)
+		return -EINVAL;
+
+	fujitsu->acpi_handle_e3 = device->handle;
+	sprintf(acpi_device_name(device), "%s",
+		ACPI_FUJITSUE3_DEVICE_NAME);
+	sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
+	acpi_driver_data(device) = fujitsu;
+
+	status = acpi_install_notify_handler(device->handle,
+					     ACPI_DEVICE_NOTIFY,
+					     acpi_fujitsu_notify_e3,
+					     fujitsu);
+
+	if (ACPI_FAILURE(status)) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Error installing notify handler\n"));
+		error = -ENODEV;
+		goto err_stop;
+	}
+
+	fujitsu->input_e3 = input = input_allocate_device();
+	if (!input) {
+		error = -ENOMEM;
+		goto err_uninstall_notify;
+	}
+
+	snprintf(fujitsu->phys_e3, sizeof(fujitsu->phys_e3),
+		 "%s/video/input0", acpi_device_hid(device));
+
+	input->name = acpi_device_name(device);
+	input->phys = fujitsu->phys_e3;
+	input->id.bustype = BUS_HOST;
+	input->id.product = 0x06;
+	input->dev.parent = &device->dev;
+	input->evbit[0] = BIT(EV_KEY);
+	set_bit(KEY_SCREENLOCK, input->keybit);
+	set_bit(KEY_MEDIA, input->keybit);
+	set_bit(KEY_EMAIL, input->keybit);
+	set_bit(KEY_SUSPEND, input->keybit);
+	set_bit(KEY_UNKNOWN, input->keybit);
+
+	error = input_register_device(input);
+	if (error)
+		goto err_free_input_dev;
+
+	result = acpi_bus_get_power(fujitsu->acpi_handle_e3, &state);
+	if (result) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Error reading power state\n"));
+		goto end;
+	}
+
+	printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
+	       acpi_device_name(device), acpi_device_bid(device),
+	       !device->power.state ? "on" : "off");
+
+	fujitsu->dev_e3 = device;
+
+	return result;
+
+      end:
+      err_free_input_dev:
+	input_free_device(input);
+      err_uninstall_notify:
+	acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
+				   acpi_fujitsu_notify_e3);
+      err_stop:
+
+	return result;
+}
+
+static int acpi_fujitsu_remove_e3(struct acpi_device *device, int type)
+{
+	acpi_status status;
+	struct fujitsu_t *fujitsu = NULL;
+
+	ACPI_FUNCTION_TRACE("acpi_fujitsu_remove_e3");
+
+	if (!device || !acpi_driver_data(device))
+		return -EINVAL;
+
+	fujitsu = acpi_driver_data(device);
+
+	status = acpi_remove_notify_handler(fujitsu->acpi_handle_e3,
+					    ACPI_DEVICE_NOTIFY,
+					    acpi_fujitsu_notify_e3);
+
+	if (!device || !acpi_driver_data(device))
+		return -EINVAL;
+
+	fujitsu->acpi_handle_e3 = 0;
+
+	return 0;
+}
+
+static const struct acpi_device_id fujitsu_device_ids_e3[] = {
+	{ACPI_FUJITSUE3_HID, 0},
+	{"", 0},
+};
+
+static struct acpi_driver acpi_fujitsu_driver_e3 = {
+	.name = ACPI_FUJITSUE3_DRIVER_NAME,
+	.class = ACPI_FUJITSU_CLASS,
+	.ids = fujitsu_device_ids_e3,
+	.ops = {
+		.add = acpi_fujitsu_add_e3,
+		.remove = acpi_fujitsu_remove_e3,
 		},
 };
 
 /* Initialization */
 
+static void
+acpi_fujitsu_notify_b1(acpi_handle handle, u32 event, void *data)
+{
+	struct input_dev *input;
+	int keycode;
+	int oldb, newb;
+
+	input = fujitsu->input_b1;
+
+	switch (event) {
+	case ACPI_FUJITSUB1_NOTIFY_CODE1:
+		oldb = fujitsu->brightness_level;
+		get_lcd_level(); /* the alt version always yields changed */
+		newb = fujitsu->brightness_level;
+
+		if (oldb == newb && fujitsu->brightness_changed) {
+			keycode = 0;
+			if (oldb == 0)
+			    keycode = KEY_BRIGHTNESSDOWN;
+			else if (oldb == (fujitsu->max_brightness)-1)
+			    keycode = KEY_BRIGHTNESSUP;
+		} else if (oldb < newb) {
+			if (fujitsu->usealt)
+				set_lcd_level_alt(newb);
+			else
+				set_lcd_level(newb);
+			acpi_bus_generate_proc_event(fujitsu->dev_b1,
+						     ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS,
+						     0);
+			keycode = KEY_BRIGHTNESSUP;
+		} else if (oldb > newb) {
+			if (fujitsu->usealt)
+				set_lcd_level_alt(newb);
+			else
+				set_lcd_level(newb);
+			acpi_bus_generate_proc_event(fujitsu->dev_b1,
+						     ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS,
+						     0);
+			keycode = KEY_BRIGHTNESSDOWN;
+		} else {
+			keycode = KEY_UNKNOWN;
+		}
+		break;
+	default:
+		keycode = KEY_UNKNOWN;
+		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+				  "Unsupported event [0x%x]\n", event));
+		break;
+	}
+
+	if (keycode != 0) {
+		input_report_key(input, keycode, 1);
+		input_sync(input);
+		input_report_key(input, keycode, 0);
+		input_sync(input);
+	}
+
+	return;
+}
+
+static void
+acpi_fujitsu_notify_e3(acpi_handle handle, u32 event, void *data)
+{
+	struct input_dev *input;
+	int keycode;
+	unsigned int irb;
+
+	input = fujitsu->input_e3;
+
+	switch (event) {
+	case ACPI_FUJITSUE3_NOTIFY_CODE1:
+		irb = get_irb();
+
+		switch (irb & 0x4ff) {
+		case LOCK_KEY:
+			keycode = KEY_SCREENLOCK;
+			break;
+		case DISPLAY_KEY:
+			keycode = KEY_MEDIA;
+			break;
+		case ENERGY_KEY:
+			keycode = KEY_EMAIL;
+			break;
+		case REST_KEY:
+			keycode = KEY_SUSPEND;
+			break;
+		default:
+			keycode = 0;
+			break;
+		}
+		break;
+	default:
+		keycode = KEY_UNKNOWN;
+		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+				  "Unsupported event [0x%x]\n", event));
+		break;
+	}
+
+	if (keycode == KEY_UNKNOWN) {
+		input_report_key(input, keycode, 1);
+		input_sync(input);
+		input_report_key(input, keycode, 0);
+		input_sync(input);
+	} else if (keycode == 0 && fujitsu->keycode_e3 != 0) {
+		input_report_key(input, fujitsu->keycode_e3, 0);
+		input_sync(input);
+		fujitsu->keycode_e3 = 0;
+	} else if (fujitsu->keycode_e3 != 0) {
+		input_report_key(input, fujitsu->keycode_e3, 0);
+		input_sync(input);
+		input_report_key(input, keycode, 1);
+		input_sync(input);
+		fujitsu->keycode_e3 = keycode;
+	} else {
+		input_report_key(input, keycode, 1);
+		input_sync(input);
+		fujitsu->keycode_e3 = keycode;
+	}
+
+	return;
+}
+
 static int __init fujitsu_init(void)
 {
-	int ret, result;
+	int ret, result, max_brightness;
 
 	if (acpi_disabled)
 		return -ENODEV;
@@ -265,21 +773,38 @@ static int __init fujitsu_init(void)
 		return -ENOMEM;
 	memset(fujitsu, 0, sizeof(struct fujitsu_t));
 
-	result = acpi_bus_register_driver(&acpi_fujitsu_driver);
+	fujitsu->usealt = use_alt_lcd_levels != 0 ? 1 : 0;
+
+	result = acpi_bus_register_driver(&acpi_fujitsu_driver_b1);
+	if (result < 0) {
+		ret = -ENODEV;
+		goto fail_acpi_b1;
+	}
+
+	result = acpi_bus_register_driver(&acpi_fujitsu_driver_e3);
 	if (result < 0) {
 		ret = -ENODEV;
-		goto fail_acpi;
+		goto fail_acpi_e3;
 	}
 
 	/* Register backlight stuff */
 
-	fujitsu->bl_device =
-	    backlight_device_register("fujitsu-laptop", NULL, NULL,
-				      &fujitsubl_ops);
+	if (fujitsu->usealt) {
+		fujitsu->bl_device =
+		    backlight_device_register("fujitsu-laptop", NULL, NULL,
+					      &fujitsubl_ops_alt);
+	} else {
+		fujitsu->bl_device =
+		    backlight_device_register("fujitsu-laptop", NULL, NULL,
+					      &fujitsubl_ops);
+	}
 	if (IS_ERR(fujitsu->bl_device))
 		return PTR_ERR(fujitsu->bl_device);
 
-	fujitsu->bl_device->props.max_brightness = FUJITSU_LCD_N_LEVELS - 1;
+	max_brightness = fujitsu->max_brightness;
+
+	fujitsu->bl_device->props.max_brightness = max_brightness - 1;
+
 	ret = platform_driver_register(&fujitsupf_driver);
 	if (ret)
 		goto fail_backlight;
@@ -323,7 +848,11 @@ static int __init fujitsu_init(void)
 
 	backlight_device_unregister(fujitsu->bl_device);
 
-      fail_acpi:
+      fail_acpi_e3:
+
+	acpi_bus_unregister_driver(&acpi_fujitsu_driver_b1);
+
+      fail_acpi_b1:
 
 	kfree(fujitsu);
 
@@ -338,7 +867,8 @@ static void __exit fujitsu_cleanup(void)
 	platform_driver_unregister(&fujitsupf_driver);
 	backlight_device_unregister(fujitsu->bl_device);
 
-	acpi_bus_unregister_driver(&acpi_fujitsu_driver);
+	acpi_bus_unregister_driver(&acpi_fujitsu_driver_e3);
+	acpi_bus_unregister_driver(&acpi_fujitsu_driver_b1);
 
 	kfree(fujitsu);
 
@@ -348,6 +878,10 @@ static void __exit fujitsu_cleanup(void)
 module_init(fujitsu_init);
 module_exit(fujitsu_cleanup);
 
+module_param(use_alt_lcd_levels, uint, 0644);
+MODULE_PARM_DESC(use_alt_lcd_levels,
+		 "Use alternative interface for lcd_levels (needed for Lifebook
s6410).");
+
 MODULE_AUTHOR("Jonathan Woithe");
 MODULE_DESCRIPTION("Fujitsu laptop extras support");
 MODULE_VERSION(FUJITSU_DRIVER_VERSION);


--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux IBM ACPI]     [Linux Power Management]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux