* [PATCH] ACPI: Support Fujitsu Lifebook S6410 in fujitsu-laptop
@ 2008-03-05 3:01 nokos
2008-04-09 23:40 ` nokos
0 siblings, 1 reply; 17+ messages in thread
From: nokos @ 2008-03-05 3:01 UTC (permalink / raw)
To: linux-acpi
Subject: [PATCH] ACPI: Support Fujitsu Lifebook S6410 in fujitsu-laptop
From: Peter Gruber <nokos@gmx.net>
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.
patch is for commit b332a60bc66bedf84ce063df33def26765c1494a
Signed-off-by: Peter Gruber <nokos@gmx.net>
diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c
--- 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
--- a/drivers/misc/fujitsu-laptop.c
+++ b/drivers/misc/fujitsu-laptop.c
@@ -2,6 +2,7 @@
/*
Copyright (C) 2007 Jonathan Woithe <jwoithe@physics.adelaide.edu.au>
+ Copyright (C) 2008 Peter Gruber <nokos@gmx.net>
Based on earlier work:
Copyright (C) 2003 Shane Spencer <shane@bogomip.com>
Adrian Yee <brewt-fujitsu@brewt.org>
@@ -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,303 @@ 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;
+
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;
+
+ 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;
+ } 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 +765,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 +840,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 +859,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 +870,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(acpi_pstate_strict,
+ "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);
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH] ACPI: Support Fujitsu Lifebook S6410 in fujitsu-laptop 2008-03-05 3:01 [PATCH] ACPI: Support Fujitsu Lifebook S6410 in fujitsu-laptop nokos @ 2008-04-09 23:40 ` nokos 2008-04-13 16:22 ` nokos 0 siblings, 1 reply; 17+ messages in thread From: nokos @ 2008-04-09 23:40 UTC (permalink / raw) To: linux-acpi 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@gmx.net> > 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@physics.adelaide.edu.au> + Copyright (C) 2008 Peter Gruber <nokos@gmx.net> Based on earlier work: Copyright (C) 2003 Shane Spencer <shane@bogomip.com> Adrian Yee <brewt-fujitsu@brewt.org> @@ -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); ^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH] ACPI: Support Fujitsu Lifebook S6410 in fujitsu-laptop 2008-04-09 23:40 ` nokos @ 2008-04-13 16:22 ` nokos 2008-04-14 10:46 ` Thomas Renninger 0 siblings, 1 reply; 17+ messages in thread From: nokos @ 2008-04-13 16:22 UTC (permalink / raw) To: linux-acpi There was an small error in the last patch, here the corrected version. Am Donnerstag, den 10.04.2008, 01:40 +0200 schrieb nokos@gmx.net: > 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@gmx.net> > > > 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 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c @@ -448,6 +448,14 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { 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 --- a/drivers/misc/fujitsu-laptop.c +++ b/drivers/misc/fujitsu-laptop.c @@ -2,6 +2,7 @@ /* Copyright (C) 2007 Jonathan Woithe <jwoithe@physics.adelaide.edu.au> + Copyright (C) 2008 Peter Gruber <nokos@gmx.net> Based on earlier work: Copyright (C) 2003 Shane Spencer <shane@bogomip.com> Adrian Yee <brewt-fujitsu@brewt.org> @@ -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,6 +56,8 @@ #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> #define FUJITSU_DRIVER_VERSION "0.3" @@ -56,24 +65,72 @@ #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 +138,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 +159,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 +245,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 +280,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 +341,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 +360,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 +393,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 +458,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 = NULL; + + 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 = NULL; 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 = NULL; + + 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 +772,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 +847,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 +866,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 +877,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); ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] ACPI: Support Fujitsu Lifebook S6410 in fujitsu-laptop 2008-04-13 16:22 ` nokos @ 2008-04-14 10:46 ` Thomas Renninger 2008-04-14 11:19 ` nokos ` (3 more replies) 0 siblings, 4 replies; 17+ messages in thread From: Thomas Renninger @ 2008-04-14 10:46 UTC (permalink / raw) To: nokos; +Cc: linux-acpi On Sun, 2008-04-13 at 18:22 +0200, nokos@gmx.net wrote: > There was an small error in the last patch, here the corrected version. IMO there should be two fujitsu drivers, one for: ACPI_FUJITSUB1_HID "FUJ02B1" e.g. acpi_fujitsu_brightness or acpi_fujitsu_02b1 and one for: ACPI_FUJITSUE3_HID "FUJ02E3" e.g. acpi_fujitsu_keys or acpi_fujitsu_02e3 not sure what the perfect name could be... There probably are machines out there which only have one of above devices, then only the relevant driver should be loaded. You should also take people into CC who are already stated as authors in the driver, chances are high that they miss this post. Someone should review this who is deeper involved into fujitsu machines. > Am Donnerstag, den 10.04.2008, 01:40 +0200 schrieb nokos@gmx.net: > > 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@gmx.net> > > > > > 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 > --- a/drivers/acpi/blacklist.c > +++ b/drivers/acpi/blacklist.c > @@ -448,6 +448,14 @@ static struct dmi_system_id acpi_osi_dmi_table[] > __initdata = { > 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"), > + }, > + }, Is there a bug for this one? If I find the time I'd like to have a closer look at acpidump and Vista vs XP changes. > /* > * Disable OSI(Linux) warnings on all "Hewlett-Packard" > * > diff --git a/drivers/misc/fujitsu-laptop.c > b/drivers/misc/fujitsu-laptop.c > --- a/drivers/misc/fujitsu-laptop.c > +++ b/drivers/misc/fujitsu-laptop.c > @@ -2,6 +2,7 @@ > > /* > Copyright (C) 2007 Jonathan Woithe <jwoithe@physics.adelaide.edu.au> > + Copyright (C) 2008 Peter Gruber <nokos@gmx.net> > Based on earlier work: > Copyright (C) 2003 Shane Spencer <shane@bogomip.com> > Adrian Yee <brewt-fujitsu@brewt.org> > @@ -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,6 +56,8 @@ > #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> > > #define FUJITSU_DRIVER_VERSION "0.3" > @@ -56,24 +65,72 @@ > #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); This is ugly, but often done/needed in vendor specific ACPI drivers. Guessing the used functions by trial and error... Have a closer look whether there are more "upper level" functions you could use to access the same info which might stay stable for this specific fujitsu device. Same for other functions below. Thomas > + 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 +138,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 +159,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 +245,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 +280,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 +341,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 +360,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 +393,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 +458,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 = NULL; > + > + 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 = NULL; > > 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 = NULL; > + > + 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 +772,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 +847,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 +866,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 +877,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@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] ACPI: Support Fujitsu Lifebook S6410 in fujitsu-laptop 2008-04-14 10:46 ` Thomas Renninger @ 2008-04-14 11:19 ` nokos 2008-04-14 12:11 ` Thomas Renninger 2008-04-15 3:14 ` Jonathan Woithe 2008-04-14 18:00 ` nokos ` (2 subsequent siblings) 3 siblings, 2 replies; 17+ messages in thread From: nokos @ 2008-04-14 11:19 UTC (permalink / raw) To: trenn; +Cc: Jonathan Woithe, linux-acpi Am Montag, den 14.04.2008, 12:46 +0200 schrieb Thomas Renninger: > On Sun, 2008-04-13 at 18:22 +0200, nokos@gmx.net wrote: > > There was an small error in the last patch, here the corrected version. > > IMO there should be two fujitsu drivers, one for: > ACPI_FUJITSUB1_HID "FUJ02B1" > e.g. acpi_fujitsu_brightness > or > acpi_fujitsu_02b1 > and one for: > ACPI_FUJITSUE3_HID "FUJ02E3" > e.g. acpi_fujitsu_keys > or > acpi_fujitsu_02e3 > not sure what the perfect name could be... > > There probably are machines out there which only have one of above > devices, then only the relevant driver should be loaded. I thought the since there is only a small functionality in each part and the module only registers drivers which are started only if an appropriate device is present it might be good to integrate it in one module. (in the S6410 case there is actually a third device FUJ02E1 but I don't know yet what it is for, there are some notifies for that device in the DSDT but so far I haven't triggered any of them) > You should also take people into CC who are already stated as authors in > the driver, chances are high that they miss this post. Someone should > review this who is deeper involved into fujitsu machines. ok. > > Am Donnerstag, den 10.04.2008, 01:40 +0200 schrieb nokos@gmx.net: > > > 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@gmx.net> > > > > > > > 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 > > --- a/drivers/acpi/blacklist.c > > +++ b/drivers/acpi/blacklist.c > > @@ -448,6 +448,14 @@ static struct dmi_system_id acpi_osi_dmi_table[] > > __initdata = { > > 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"), > > + }, > > + }, > Is there a bug for this one? > If I find the time I'd like to have a closer look at acpidump and Vista > vs XP changes. The problem is this: The brightness adjustment stuff in the DSDT is: Method (_L1C, 0, NotSerialized) { Store (SSF0, Local0) Store (Local0, SSF0) And (Local0, Not (SSM0), Local0) Store (SSF1, Local1) Store (Local1, SSF1) And (Local1, Not (SSM1), Local1) Store (SSF2, Local2) Store (Local2, SSF2) And (Local2, Not (SSM2), Local2) If (And (Local0, 0x01)) { \_SB.PCI0.GFX0.PHTK () Store (0x80, BCMD) Store (Zero, SMIC) \_SB.PCI0.GFX0.AHTK () If (BSWF) { If (LGreaterEqual (\_SB.OSTP (), 0x40)) { \_SB.PCI0.GFX0.LCD.BLNF () } Else { Name (BLTS, Zero) ... the \_SB.OSTP ()is the switch controlled by osi vista (0x40 for windows 2006) and the _SB.PCI0.GFX0.LCD.BLNF () is in an external table which has signature "\0\0\0\0" and hence is not loaded by the linux acpi system. Interestingly if you force it to be loaded (eg. by rewriting the signature in drivers/acpi/tables/tbinstal.c) the linux acpi system detects brightness control via ACPI_VIDEO but doesn't do anything if I try to change the brightness ... Also since the additional table has no signature acpidump does not dump it ... > > /* > > * Disable OSI(Linux) warnings on all "Hewlett-Packard" > > * > > diff --git a/drivers/misc/fujitsu-laptop.c > > b/drivers/misc/fujitsu-laptop.c > > --- a/drivers/misc/fujitsu-laptop.c > > +++ b/drivers/misc/fujitsu-laptop.c > > @@ -2,6 +2,7 @@ > > > > /* > > Copyright (C) 2007 Jonathan Woithe <jwoithe@physics.adelaide.edu.au> > > + Copyright (C) 2008 Peter Gruber <nokos@gmx.net> > > Based on earlier work: > > Copyright (C) 2003 Shane Spencer <shane@bogomip.com> > > Adrian Yee <brewt-fujitsu@brewt.org> > > @@ -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,6 +56,8 @@ > > #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> > > > > #define FUJITSU_DRIVER_VERSION "0.3" > > @@ -56,24 +65,72 @@ > > #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); > This is ugly, but often done/needed in vendor specific ACPI drivers. > Guessing the used functions by trial and error... > Have a closer look whether there are more "upper level" functions you > could use to access the same info which might stay stable for this > specific fujitsu device. > Same for other functions below. > > Thomas > > > > > > + 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 +138,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 +159,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 +245,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 +280,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 +341,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 +360,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 +393,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 +458,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 = NULL; > > + > > + 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 = NULL; > > > > 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 = NULL; > > + > > + 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 +772,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 +847,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 +866,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 +877,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@vger.kernel.org > > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] ACPI: Support Fujitsu Lifebook S6410 in fujitsu-laptop 2008-04-14 11:19 ` nokos @ 2008-04-14 12:11 ` Thomas Renninger 2008-04-15 3:14 ` Jonathan Woithe 1 sibling, 0 replies; 17+ messages in thread From: Thomas Renninger @ 2008-04-14 12:11 UTC (permalink / raw) To: nokos; +Cc: Jonathan Woithe, linux-acpi, Moore, Robert, Lin, Ming M Hi, Robert/Ming: you might be interested in the broken SSDT table on latest Fujitsus mentioned at the end of this mail. On Mon, 2008-04-14 at 13:19 +0200, nokos@gmx.net wrote: > Am Montag, den 14.04.2008, 12:46 +0200 schrieb Thomas Renninger: > > On Sun, 2008-04-13 at 18:22 +0200, nokos@gmx.net wrote: > > > There was an small error in the last patch, here the corrected version. > > > > IMO there should be two fujitsu drivers, one for: > > ACPI_FUJITSUB1_HID "FUJ02B1" > > e.g. acpi_fujitsu_brightness > > or > > acpi_fujitsu_02b1 > > and one for: > > ACPI_FUJITSUE3_HID "FUJ02E3" > > e.g. acpi_fujitsu_keys > > or > > acpi_fujitsu_02e3 > > not sure what the perfect name could be... > > > > There probably are machines out there which only have one of above > > devices, then only the relevant driver should be loaded. > > I thought the since there is only a small functionality in each part and > the module only registers drivers which are started only if an > appropriate device is present it might be good to integrate it in one > module. (in the S6410 case there is actually a third device FUJ02E1 but > I don't know yet what it is for, there are some notifies for that device > in the DSDT but so far I haven't triggered any of them) One driver per device is the way to go IMO. If you know that these devices are always present together..., but even then it is easier to review/read and makes things more modular, e.g. you can let the one driver depend on backlight, the other one input or whatever other needed subsystems and e.g. only compile out brightness support if you like to (may be convenient for backlight being managed via video driver at some time), but still make use of the the hotkey driver. > The problem is this: > The brightness adjustment stuff in the DSDT is: > Method (_L1C, 0, NotSerialized) > { > Store (SSF0, Local0) > Store (Local0, SSF0) > And (Local0, Not (SSM0), Local0) > Store (SSF1, Local1) > Store (Local1, SSF1) > And (Local1, Not (SSM1), Local1) > Store (SSF2, Local2) > Store (Local2, SSF2) > And (Local2, Not (SSM2), Local2) > If (And (Local0, 0x01)) > { > \_SB.PCI0.GFX0.PHTK () > Store (0x80, BCMD) > Store (Zero, SMIC) > \_SB.PCI0.GFX0.AHTK () > If (BSWF) > { > If (LGreaterEqual (\_SB.OSTP (), 0x40)) > { > \_SB.PCI0.GFX0.LCD.BLNF () > } > Else > { > Name (BLTS, Zero) > ... > the \_SB.OSTP ()is the switch controlled by osi vista (0x40 for windows > 2006) > and the _SB.PCI0.GFX0.LCD.BLNF () is in an external table which has > signature "\0\0\0\0" and hence is not loaded by the linux acpi system. > Interestingly if you force it to be loaded (eg. by rewriting the > signature in drivers/acpi/tables/tbinstal.c) the linux acpi system > detects brightness control via ACPI_VIDEO but doesn't do anything if I > try to change the brightness ... > > Also since the additional table has no signature acpidump does not dump > it ... Ahhh, this one. This is related to: http://bugzilla.kernel.org/show_bug.cgi?id=9919 In fact the bug is about another problem, but also shows this issue. Instead of mixing things up in this bug, could you open another one and attach acpidump and the acpidump --addr xy --length yx output of the broken "visit" table (you can take over the address and length of the OperationRegion), pls. Can you then give the other guys in above bug a pointer to the newly created and add Robert.Moore@intel.com and ming.m.lin@intel.com and me to the list of CC'ed people. They have to decide how to deal with this (probably) BIOS bug. I could imagine a solution similar to: The signature is ignored for tables loaded via "load" ASL table runtime loading commands in SLACk mode. There were a lot table loading fixes posted for 2.6.26, but I doubt they cover this bug. An already uploaded broken Fujitsu SSDT(but mixed with another bug) can be found here (comment #15): http://bugzilla.kernel.org/show_bug.cgi?id=9919#c15 Thanks, Thomas ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] ACPI: Support Fujitsu Lifebook S6410 in fujitsu-laptop 2008-04-14 11:19 ` nokos 2008-04-14 12:11 ` Thomas Renninger @ 2008-04-15 3:14 ` Jonathan Woithe 2008-04-15 10:51 ` nokos 1 sibling, 1 reply; 17+ messages in thread From: Jonathan Woithe @ 2008-04-15 3:14 UTC (permalink / raw) To: nokos; +Cc: trenn, Jonathan Woithe, linux-acpi > > There probably are machines out there which only have one of above > > devices, then only the relevant driver should be loaded. > > I thought the since there is only a small functionality in each part and > the module only registers drivers which are started only if an > appropriate device is present it might be good to integrate it in one > module. (in the S6410 case there is actually a third device FUJ02E1 but > I don't know yet what it is for, there are some notifies for that device > in the DSDT but so far I haven't triggered any of them) A fair point. I guess it really depends on how these devices end up being structured. Modularity enforces separation which can help with maintainability going forward but it can also give rise to a certain amount of code duplication. I'd be interested to see what others think. Regards jonathan ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] ACPI: Support Fujitsu Lifebook S6410 in fujitsu-laptop 2008-04-15 3:14 ` Jonathan Woithe @ 2008-04-15 10:51 ` nokos 2008-04-15 23:21 ` Jonathan Woithe 0 siblings, 1 reply; 17+ messages in thread From: nokos @ 2008-04-15 10:51 UTC (permalink / raw) To: linux-acpi; +Cc: trenn, Jonathan Woithe Am Dienstag, den 15.04.2008, 12:44 +0930 schrieb Jonathan Woithe: > > > There probably are machines out there which only have one of above > > > devices, then only the relevant driver should be loaded. > > > > I thought the since there is only a small functionality in each part and > > the module only registers drivers which are started only if an > > appropriate device is present it might be good to integrate it in one > > module. (in the S6410 case there is actually a third device FUJ02E1 but > > I don't know yet what it is for, there are some notifies for that device > > in the DSDT but so far I haven't triggered any of them) > > A fair point. I guess it really depends on how these devices end up being > structured. Modularity enforces separation which can help with > maintainability going forward but it can also give rise to a certain > amount of code duplication. I'd be interested to see what others think. I had another look into the DSDT and this is how the structure looks to me: I have three devices (FUJ02B1, FUJ02E1, FUJ02E3) with names (FJEX, CMBT, FEXT) so far it seems we can ignore the second since it always reports _STA=0 (ie. disabled?) FUJ02B1 Methods: - RBLL get max brightness level - GBLL/GBLS get current brightness (only difference GBLL zeros a flag if used for the first time which is not used anywhere else?) - SBLL/SBL2 set brightness (some differences, notable SBLL doesn't do anything if a flag is set but interacts with the video device GFX0) - GMOU,SMOU some getter/setter of unknown flags - GHKS,GSIF more unknown flags FUJ02E3 Methods: - FUNC frontend to S000-S009 : approx. FUNC(0x1000+i,j,k,l) = S00i(j,k,l) - S000-S009 getters and setters for various stuff (not restricted to FUJ02E3) - S000 uses some fields from FUJ02E1 - S004 clones GIRB - S006 seems to be a frontend for video/brightness stuff in FUJ02B1 and FUJ02E3 - GIRB/SIRB get and set? the special hotkey registers - SBLC/SVBL/RSBL do something with brightness and/or video using FUJ02B1 methods so it seems the devices are not independent of each other and a combined module might be the more sensible way to go? Peter ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] ACPI: Support Fujitsu Lifebook S6410 in fujitsu-laptop 2008-04-15 10:51 ` nokos @ 2008-04-15 23:21 ` Jonathan Woithe 0 siblings, 0 replies; 17+ messages in thread From: Jonathan Woithe @ 2008-04-15 23:21 UTC (permalink / raw) To: nokos; +Cc: linux-acpi, trenn, Jonathan Woithe Hi Peter > Am Dienstag, den 15.04.2008, 12:44 +0930 schrieb Jonathan Woithe: > > > > There probably are machines out there which only have one of above > > > > devices, then only the relevant driver should be loaded. > > > > > > I thought the since there is only a small functionality in each part and > > > the module only registers drivers which are started only if an > > > appropriate device is present it might be good to integrate it in one > > > module. (in the S6410 case there is actually a third device FUJ02E1 but > > > I don't know yet what it is for, there are some notifies for that device > > > in the DSDT but so far I haven't triggered any of them) > > > > A fair point. I guess it really depends on how these devices end up being > > structured. Modularity enforces separation which can help with > > maintainability going forward but it can also give rise to a certain > > amount of code duplication. I'd be interested to see what others think. > > I had another look into the DSDT and this is how the structure looks to > me: > I have three devices (FUJ02B1, FUJ02E1, FUJ02E3) with names (FJEX, CMBT, > FEXT) > so far it seems we can ignore the second since it always reports _STA=0 > (ie. disabled?) > > FUJ02B1 Methods: > - RBLL get max brightness level > - GBLL/GBLS get current brightness (only difference GBLL zeros a flag > if used for the first time which is not used anywhere else?) > - SBLL/SBL2 set brightness (some differences, notable SBLL doesn't do > anything if a flag is set but interacts with the video device GFX0) > - GMOU,SMOU some getter/setter of unknown flags > - GHKS,GSIF more unknown flags > > FUJ02E3 Methods: > - FUNC frontend to S000-S009 : approx. FUNC(0x1000+i,j,k,l) = > S00i(j,k,l) > - S000-S009 getters and setters for various stuff (not restricted to > FUJ02E3) > - S000 uses some fields from FUJ02E1 > - S004 clones GIRB > - S006 seems to be a frontend for video/brightness stuff in FUJ02B1 > and FUJ02E3 > - GIRB/SIRB get and set? the special hotkey registers > - SBLC/SVBL/RSBL do something with brightness and/or video using > FUJ02B1 methods > > so it seems the devices are not independent of each other and a combined > module might be the more sensible way to go? Maybe. Let's see how the patch behaves on the S7020 first and take things from there. From the above it seems to me that FUJ02B1 is the thing which deals direct with brightness and that any brightness-related stuff in FUJ02E3 may be there for convenience. If that was the case then the modular split still makes sense I think. If we can show that the FUJ02E3 methods add additional functionality on top of that provided by FUJ02B1 then a single module may make sense. For example, maybe FUJ02E3's SBLC/SVBL/RSBL take care of selecting the correct low-level brightness control method on a given laptop. Of course if laptops turn up without the FUJ02E3 it might complicate things somewhat. If we were to go to a single module we would have to find a way to automatically work out which brightness control method to use. Otherwise almost every user of the module will have to worry about setting module parameters because there's no default setting which would work for "most" users. I'll let you know how the current patch behaves on the S7020. Regards jonathan ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] ACPI: Support Fujitsu Lifebook S6410 in fujitsu-laptop 2008-04-14 10:46 ` Thomas Renninger 2008-04-14 11:19 ` nokos @ 2008-04-14 18:00 ` nokos [not found] ` <1208193500.11305.17.camel@earth.gruber.myown> 2008-04-15 3:11 ` Jonathan Woithe 3 siblings, 0 replies; 17+ messages in thread From: nokos @ 2008-04-14 18:00 UTC (permalink / raw) To: linux-acpi Am Montag, den 14.04.2008, 12:46 +0200 schrieb Thomas Renninger: > > > > +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); > This is ugly, but often done/needed in vendor specific ACPI drivers. > Guessing the used functions by trial and error... > Have a closer look whether there are more "upper level" functions you > could use to access the same info which might stay stable for this > specific fujitsu device. > Same for other functions below. looking through the DSDT it is only use in the S002 function and this one is only used in FUNC (which seems to be some kind of frontend for all the S000 - S009 functions with even more cryptic parameters, ie. FUNC(0x1002,1,0,0) is the same as GIRB()) The other functions used are SBL2, which is the same as SBLL except the Method (SBLL, 1, NotSerialized) { If (NGTF) { Store (Arg0, LSBL) Return (Zero) } ... clause which seems to stop SBLL from working and GBLS which is a variant of GBLL with similar problem. The whole point of the parameter use_alt_lcd_levels was to have an easy way to switch between SBLL/GBLL (not working on S6410) and SBL2/GBLS (working on S6410) Peter > > > > -- > > To unsubscribe from this list: send the line "unsubscribe linux-acpi" in > > the body of a message to majordomo@vger.kernel.org > > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 17+ messages in thread
[parent not found: <1208193500.11305.17.camel@earth.gruber.myown>]
[parent not found: <1208195761.1784.95.camel@queen.suse.de>]
* Re: [PATCH] ACPI: Support Fujitsu Lifebook S6410 in fujitsu-laptop [not found] ` <1208195761.1784.95.camel@queen.suse.de> @ 2008-04-14 18:22 ` nokos 2008-04-14 19:03 ` Thomas Renninger 2008-04-14 19:18 ` Thomas Renninger 2008-04-15 9:36 ` nokos 1 sibling, 2 replies; 17+ messages in thread From: nokos @ 2008-04-14 18:22 UTC (permalink / raw) To: trenn; +Cc: linux-acpi Am Montag, den 14.04.2008, 19:56 +0200 schrieb Thomas Renninger: > On Mon, 2008-04-14 at 19:18 +0200, nokos@gmx.net wrote: > > Am Montag, den 14.04.2008, 12:46 +0200 schrieb Thomas Renninger: > > > > > > > > +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); > > > This is ugly, but often done/needed in vendor specific ACPI drivers. > > > Guessing the used functions by trial and error... > > > Have a closer look whether there are more "upper level" functions you > > > could use to access the same info which might stay stable for this > > > specific fujitsu device. > > > Same for other functions below. > > > > looking through the DSDT it is only use in the S002 function and this > > one is only used in FUNC (which seems to be some kind of frontend for > > all the S000 - S009 functions with even more cryptic parameters, ie. > > FUNC(0x1002,1,0,0) is the same as GIRB()) > > > > The other functions used are SBL2, which is the same as SBLL except the > > Method (SBLL, 1, NotSerialized) > > { > > If (NGTF) > > { > > Store (Arg0, LSBL) > > Return (Zero) > > } > > ... > > clause which seems to stop SBLL from working and GBLS which is a variant of GBLL with similar problem. > > The whole point of the parameter use_alt_lcd_levels was to have an easy way to switch between > > SBLL/GBLL (not working on S6410) and SBL2/GBLS (working on S6410) > > Hmm, you mean because NGTF has another value on your machine? > Trying to get this solved automatically would be great... I have not yet tried to readout the NGTF with a platform device. Hmm, how do access fields like NGTF (All I have done so far was calling functions like GBLL() and examining the return value). According to the DSDT should FUNC(0x1004,1,4,0) switch off NGTF, but then SBLL is still not working. Where can I find some documentation of the DSL/ASL language? Are there docs about the linux-acpi interface? > I know there is an asus_acpi driver mailing list, I do not know about a > Fujitu one. > > I do not have that much time currently, if you like I can go through the > code again after you split it into two parts. > Then it should be much easier to read... already done ... will test it bit and then post it here. > IMO we should also document our thinking, maybe not on a mailing list, > but more on bugzilla.kernel.org. > Then other Fujitsu users may show up testing or providing more DSDTs > etc. and at after some code iterations Len might accept a new Fujitsu > driver. should I open a bug "fujitsu-laptop not working on S6410" or something for further discussions? > Not sure whether it's worth it, but possibly a fujitsu ACPI mailinglist > on sourcefore or elsewhere might be worth it (but expect some amount of > work...). Maybe you the author of the other fujitsu driver or others are > willing to help... > > I wonder whether you have fan problems? > I recently found this (probably older?) fujitsu bug: > http://bugzilla.kernel.org/show_bug.cgi?id=8049 no, fan works as expected although I have no controls in /proc/acpi/fan/ Peter ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] ACPI: Support Fujitsu Lifebook S6410 in fujitsu-laptop 2008-04-14 18:22 ` nokos @ 2008-04-14 19:03 ` Thomas Renninger 2008-04-14 19:18 ` Thomas Renninger 1 sibling, 0 replies; 17+ messages in thread From: Thomas Renninger @ 2008-04-14 19:03 UTC (permalink / raw) To: nokos; +Cc: linux-acpi On Mon, 2008-04-14 at 20:22 +0200, nokos@gmx.net wrote: > Am Montag, den 14.04.2008, 19:56 +0200 schrieb Thomas Renninger: > > On Mon, 2008-04-14 at 19:18 +0200, nokos@gmx.net wrote: > > > Am Montag, den 14.04.2008, 12:46 +0200 schrieb Thomas Renninger: > > > > > > > > > > +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); > > > > This is ugly, but often done/needed in vendor specific ACPI drivers. > > > > Guessing the used functions by trial and error... > > > > Have a closer look whether there are more "upper level" functions you > > > > could use to access the same info which might stay stable for this > > > > specific fujitsu device. > > > > Same for other functions below. > > > > > > looking through the DSDT it is only use in the S002 function and this > > > one is only used in FUNC (which seems to be some kind of frontend for > > > all the S000 - S009 functions with even more cryptic parameters, ie. > > > FUNC(0x1002,1,0,0) is the same as GIRB()) > > > > > > The other functions used are SBL2, which is the same as SBLL except the > > > Method (SBLL, 1, NotSerialized) > > > { > > > If (NGTF) > > > { > > > Store (Arg0, LSBL) > > > Return (Zero) > > > } > > > ... > > > clause which seems to stop SBLL from working and GBLS which is a variant of GBLL with similar problem. > > > The whole point of the parameter use_alt_lcd_levels was to have an easy way to switch between > > > SBLL/GBLL (not working on S6410) and SBL2/GBLS (working on S6410) > > > > Hmm, you mean because NGTF has another value on your machine? > > Trying to get this solved automatically would be great... > > I have not yet tried to readout the NGTF with a platform device. Hmm, > how do access fields like NGTF (All I have done so far was calling > functions like GBLL() and examining the return value). > > According to the DSDT should FUNC(0x1004,1,4,0) switch off NGTF, but > then SBLL is still not working. I hoped NGTF is accessed somewhere else? Maybe in some kind of initializing function or whatever... > > Where can I find some documentation of the DSL/ASL language? Are there > docs about the linux-acpi interface? > > > I know there is an asus_acpi driver mailing list, I do not know about a > > Fujitu one. > > > > I do not have that much time currently, if you like I can go through the > > code again after you split it into two parts. > > Then it should be much easier to read... > > already done ... will test it bit and then post it here. > > > IMO we should also document our thinking, maybe not on a mailing list, > > but more on bugzilla.kernel.org. > > Then other Fujitsu users may show up testing or providing more DSDTs > > etc. and at after some code iterations Len might accept a new Fujitsu > > driver. > > should I open a bug "fujitsu-laptop not working on S6410" or something > for further discussions? This sounds like a good a idea. Maybe a second eye on the acpi tables finds something to simplify the brightness code. Thomas > > Not sure whether it's worth it, but possibly a fujitsu ACPI mailinglist > > on sourcefore or elsewhere might be worth it (but expect some amount of > > work...). Maybe you the author of the other fujitsu driver or others are > > willing to help... > > > > I wonder whether you have fan problems? > > I recently found this (probably older?) fujitsu bug: > > http://bugzilla.kernel.org/show_bug.cgi?id=8049 > > no, fan works as expected although I have no controls in /proc/acpi/fan/ ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] ACPI: Support Fujitsu Lifebook S6410 in fujitsu-laptop 2008-04-14 18:22 ` nokos 2008-04-14 19:03 ` Thomas Renninger @ 2008-04-14 19:18 ` Thomas Renninger 1 sibling, 0 replies; 17+ messages in thread From: Thomas Renninger @ 2008-04-14 19:18 UTC (permalink / raw) To: nokos; +Cc: linux-acpi On Mon, 2008-04-14 at 20:22 +0200, nokos@gmx.net wrote: > > > clause which seems to stop SBLL from working and GBLS which is a variant of GBLL with similar problem. > > > The whole point of the parameter use_alt_lcd_levels was to have an easy way to switch between > > > SBLL/GBLL (not working on S6410) and SBL2/GBLS (working on S6410) > > > > Hmm, you mean because NGTF has another value on your machine? > > Trying to get this solved automatically would be great... > > I have not yet tried to readout the NGTF with a platform device. Hmm, > how do access fields like NGTF (All I have done so far was calling > functions like GBLL() and examining the return value). > > According to the DSDT should FUNC(0x1004,1,4,0) switch off NGTF, but > then SBLL is still not working. > > Where can I find some documentation of the DSL/ASL language? Are there > docs about the linux-acpi interface? All the docs you need and AFAIK the only document is the ACPI spec itself: www.acpi.info Hmm, could it be that this site is not accessible (already for some time)? You could disassemble, modify and override your DSDT via initrd or compiling into the kernel. Before add: Store (NGTF, Debug) statements into the DSDT, compile the kernel with ACPI_DEBUG=y and increase the debug level acpi.debug_level=0x1F debug object should be added: cat /sys/module/acpi/parameters/debug_level to modify the value at runtime echo the mask in there directly: echo 0x1F> /sys/module/acpi/parameters/debug_level for example will increase the debug level up to INFO. There is also a convenient ASL function tracing debug facility in recent kernels, but I haven't used it yet. AFAIK you echo the ASL function in there..., the git commit for that has some more info: commit 4169c45f179e285feac6bcf25f4bd0db6b109bab Author: Zhang Rui <rui.zhang@intel.com> Date: Wed Nov 14 19:38:40 2007 -0500 ACPI: add control method tracing support Add debug tracing support during certain AML method execution. Four more module parameters are created under /sys/module/acpi/parameters/: trace_method_name: the AML method name that user wants to trace trace_debug_layer: the temporary debug_layer used when tracing the method. Using 0xffffffff by default if it is 0. trace_debug_level: the temporary debug_level used when tracing the method. Using 0x00ffffff by default if it is 0. trace_state: The status of the tracing feature. "enabled" means this feature is enabled and the AML method is traced every time it's executed. "1" means this feature is enabled and the AML method will only be traced during the next execution. "disabled" means this feature is disabled. Users can enable/disable this debug tracing feature by "echo string > /sys/module/acpi/parameters/trace_state". "string" should be one of "enable", "disable" and "1". Like that you could increase a specific function to a specific debug level without getting Mega Bytes of output and you might be able to read out data without rebooting and re-overriding the DSDT... Thomas ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] ACPI: Support Fujitsu Lifebook S6410 in fujitsu-laptop [not found] ` <1208195761.1784.95.camel@queen.suse.de> 2008-04-14 18:22 ` nokos @ 2008-04-15 9:36 ` nokos 2008-04-15 23:12 ` Jonathan Woithe 1 sibling, 1 reply; 17+ messages in thread From: nokos @ 2008-04-15 9:36 UTC (permalink / raw) To: linux-acpi; +Cc: Jonathan Woithe Here is a modularized version of the patch, it also includes autodetection of S6410 to switch between the brightness interface (can be overridden by module parameter) Jonathan can you test it if it doesn't break anything on the S7020? Am Montag, den 14.04.2008, 19:56 +0200 schrieb Thomas Renninger: > I do not have that much time currently, if you like I can go through the > code again after you split it into two parts. > Then it should be much easier to read... > diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c index ea92bac..b2a73fa 100644 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c @@ -448,6 +448,14 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { 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/Kconfig b/drivers/misc/Kconfig index 962817e..105490e 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -155,6 +155,17 @@ config FUJITSU_LAPTOP If you have a Fujitsu laptop, say Y or M here. +config FUJITSU_LAPTOP_HOTKEY + tristate "Fujitsu Laptop Hotkeys" + depends on X86 + depends on ACPI + ---help--- + This is a driver for the hotkeys on laptops built by Fujitsu: + + * Tested with S6410 + + If you have a S6410 Fujitsu laptop, say Y or M here. + config TC1100_WMI tristate "HP Compaq TC1100 Tablet WMI Extras (EXPERIMENTAL)" depends on X86 && !X86_64 diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 3b12f5d..ff8e88e 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_SGI_IOC4) += ioc4.o obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o +obj-$(CONFIG_FUJITSU_LAPTOP_HOTKEY) += fujitsu-laptop-hotkey.o obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o diff --git a/drivers/misc/fujitsu-laptop-hotkey.c b/drivers/misc/fujitsu-laptop-hotkey.c new file mode 100644 index 0000000..9137536 --- /dev/null +++ b/drivers/misc/fujitsu-laptop-hotkey.c @@ -0,0 +1,321 @@ +/*-*-linux-c-*-*/ + +/* + Copyright (C) 2008 Peter Gruber <nokos@gmx.net> + + Templated from fujitsu-laptop.c which is copyright by its respective authors. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + */ + +/* + * fujitsu-laptop-hotkey.c - Fujitsu laptop hotkey support, providing access to + * additional features made available on a range of Fujitsu laptops including + * the S6xxx series. + * + * This driver has been tested on a Fujitsu Lifebook S6410. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/acpi.h> +#include <linux/dmi.h> +#include <linux/input.h> + +#define FUJITSU_DRIVER_VERSION "0.3" + +#define ACPI_FUJITSU_CLASS "fujitsu" + +#define ACPI_FUJITSUE3_DRIVER_NAME "Fujitsu laptop ACPI extras hotkey driver" +#define ACPI_FUJITSUE3_HID "FUJ02E3" +#define ACPI_FUJITSUE3_DEVICE_NAME "Fujitsu FUJ02E3" + +#define ACPI_FUJITSUE3_NOTIFY_CODE1 0x80 + +#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 { + int usealt; /* use the alternative brightness interface */ + acpi_handle acpi_handle; + struct acpi_device *dev; + struct input_dev *input; + char phys[32]; + struct platform_device *pf_device; + int keycode; /* remember keycode for release */ + + unsigned int irb; /* info about the pressed buttons */ +}; + +static struct fujitsu_t *fujitsu; + +static void acpi_fujitsu_notify(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, "GIRB", NULL, + &state); + if (status < 0) + return status; + + fujitsu->irb = state; + + return fujitsu->irb; +} + +/* ACPI device */ + +static int acpi_fujitsu_add(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"); + + if (!device) + return -EINVAL; + + fujitsu->acpi_handle = 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, + fujitsu); + + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error installing notify handler\n")); + error = -ENODEV; + goto err_stop; + } + + fujitsu->input = input = input_allocate_device(); + if (!input) { + error = -ENOMEM; + goto err_uninstall_notify; + } + + snprintf(fujitsu->phys, sizeof(fujitsu->phys), + "%s/video/input0", acpi_device_hid(device)); + + input->name = acpi_device_name(device); + input->phys = fujitsu->phys; + 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, &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 = 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); + err_stop: + + return result; +} + +static int acpi_fujitsu_remove(struct acpi_device *device, int type) +{ + acpi_status status; + struct fujitsu_t *fujitsu = NULL; + + ACPI_FUNCTION_TRACE("acpi_fujitsu_remove"); + + if (!device || !acpi_driver_data(device)) + return -EINVAL; + + fujitsu = acpi_driver_data(device); + + status = acpi_remove_notify_handler(fujitsu->acpi_handle, + ACPI_DEVICE_NOTIFY, + acpi_fujitsu_notify); + + if (!device || !acpi_driver_data(device)) + return -EINVAL; + + fujitsu->acpi_handle = NULL; + + return 0; +} + +static const struct acpi_device_id fujitsu_device_ids[] = { + {ACPI_FUJITSUE3_HID, 0}, + {"", 0}, +}; + +static struct acpi_driver acpi_fujitsu_driver = { + .name = ACPI_FUJITSUE3_DRIVER_NAME, + .class = ACPI_FUJITSU_CLASS, + .ids = fujitsu_device_ids, + .ops = { + .add = acpi_fujitsu_add, + .remove = acpi_fujitsu_remove, + }, +}; + +/* Initialization */ + +static void +acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data) +{ + struct input_dev *input; + int keycode; + unsigned int irb; + + input = fujitsu->input; + + 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 != 0) { + input_report_key(input, fujitsu->keycode, 0); + input_sync(input); + fujitsu->keycode = 0; + } else if (fujitsu->keycode != 0) { + input_report_key(input, fujitsu->keycode, 0); + input_sync(input); + input_report_key(input, keycode, 1); + input_sync(input); + fujitsu->keycode = keycode; + } else { + input_report_key(input, keycode, 1); + input_sync(input); + fujitsu->keycode = keycode; + } + + return; +} + +static int __init fujitsu_init(void) +{ + int ret, result; + + if (acpi_disabled) + return -ENODEV; + + fujitsu = kmalloc(sizeof(struct fujitsu_t), GFP_KERNEL); + if (!fujitsu) + return -ENOMEM; + memset(fujitsu, 0, sizeof(struct fujitsu_t)); + + result = acpi_bus_register_driver(&acpi_fujitsu_driver); + if (result < 0) { + ret = -ENODEV; + goto fail_acpi; + } + + printk(KERN_INFO "fujitsu-laptop-hotkey: driver " FUJITSU_DRIVER_VERSION + " successfully loaded.\n"); + + return 0; + + fail_acpi: + + kfree(fujitsu); + + return ret; +} + +static void __exit fujitsu_cleanup(void) +{ + acpi_bus_unregister_driver(&acpi_fujitsu_driver); + + kfree(fujitsu); + + printk(KERN_INFO "fujitsu-laptop-hotkey: driver unloaded.\n"); +} + +module_init(fujitsu_init); +module_exit(fujitsu_cleanup); + +MODULE_AUTHOR("Peter Gruber"); +MODULE_DESCRIPTION("Fujitsu laptop hotkey support"); +MODULE_VERSION(FUJITSU_DRIVER_VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/fujitsu-laptop.c b/drivers/misc/fujitsu-laptop.c index e2e7c05..9a80d29 100644 --- a/drivers/misc/fujitsu-laptop.c +++ b/drivers/misc/fujitsu-laptop.c @@ -2,6 +2,7 @@ /* Copyright (C) 2007 Jonathan Woithe <jwoithe@physics.adelaide.edu.au> + Copyright (C) 2008 Peter Gruber <nokos@gmx.net> Based on earlier work: Copyright (C) 2003 Shane Spencer <shane@bogomip.com> Adrian Yee <brewt-fujitsu@brewt.org> @@ -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,6 +56,8 @@ #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> #define FUJITSU_DRIVER_VERSION "0.3" @@ -56,21 +65,36 @@ #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" +#define ACPI_FUJITSUB1_HID "FUJ02B1" +#define ACPI_FUJITSUB1_DEVICE_NAME "Fujitsu FUJ02B1" + +#define ACPI_FUJITSUB1_NOTIFY_CODE1 0x80 + +#define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x86 +#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x87 struct fujitsu_t { + int usealt; /* use the alternative brightness interface */ acpi_handle acpi_handle; + struct acpi_device *dev; + struct input_dev *input; + char phys[32]; struct backlight_device *bl_device; struct platform_device *pf_device; - 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 = -1; + +static void acpi_fujitsu_notify(acpi_handle handle, u32 event, + void *data); /* Hardware access */ @@ -81,7 +105,7 @@ 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) @@ -102,18 +126,45 @@ 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, "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, "GBLL", NULL, + &state); if (status < 0) return status; - fujitsu->fuj02b1_state = state; fujitsu->brightness_level = state & 0x0fffffff; if (state & 0x80000000) @@ -124,8 +175,61 @@ static int get_lcd_level(void) 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, "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, "GBLS", 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; +} + + /* 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 +247,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 +308,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 +327,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 }; @@ -196,8 +362,11 @@ static struct platform_driver fujitsupf_driver = { static int acpi_fujitsu_add(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"); @@ -205,10 +374,46 @@ static int acpi_fujitsu_add(struct acpi_device *device) return -EINVAL; fujitsu->acpi_handle = device->handle; - sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_DEVICE_NAME); + sprintf(acpi_device_name(device), "%s", + ACPI_FUJITSUB1_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, + fujitsu); + + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error installing notify handler\n")); + error = -ENODEV; + goto err_stop; + } + + fujitsu->input = input = input_allocate_device(); + if (!input) { + error = -ENOMEM; + goto err_uninstall_notify; + } + + snprintf(fujitsu->phys, sizeof(fujitsu->phys), + "%s/video/input0", acpi_device_hid(device)); + + input->name = acpi_device_name(device); + input->phys = fujitsu->phys; + 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, &state); if (result) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, @@ -220,29 +425,55 @@ 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 = 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); + err_stop: return result; } static int acpi_fujitsu_remove(struct acpi_device *device, int type) { + acpi_status status; + struct fujitsu_t *fujitsu = NULL; + ACPI_FUNCTION_TRACE("acpi_fujitsu_remove"); if (!device || !acpi_driver_data(device)) return -EINVAL; + + fujitsu = acpi_driver_data(device); + + status = acpi_remove_notify_handler(fujitsu->acpi_handle, + ACPI_DEVICE_NOTIFY, + acpi_fujitsu_notify); + + if (!device || !acpi_driver_data(device)) + return -EINVAL; + fujitsu->acpi_handle = NULL; return 0; } static const struct acpi_device_id fujitsu_device_ids[] = { - {ACPI_FUJITSU_HID, 0}, + {ACPI_FUJITSUB1_HID, 0}, {"", 0}, }; static struct acpi_driver acpi_fujitsu_driver = { - .name = ACPI_FUJITSU_DRIVER_NAME, + .name = ACPI_FUJITSUB1_DRIVER_NAME, .class = ACPI_FUJITSU_CLASS, .ids = fujitsu_device_ids, .ops = { @@ -253,9 +484,88 @@ static struct acpi_driver acpi_fujitsu_driver = { /* Initialization */ +static int dmi_check_cb(const struct dmi_system_id *id) +{ + printk("fujitsu-laptop: Identified laptop model '%s'.\n", id->ident); + return 0; +} + +static struct dmi_system_id __initdata fujitsu_dmi_table[] = { + { + .ident = "Fujitsu Siemens", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6410"), + }, + .callback = dmi_check_cb + }, + { } +}; + + +static void +acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data) +{ + struct input_dev *input; + int keycode; + int oldb, newb; + + input = fujitsu->input; + + 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, + 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, + 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 int __init fujitsu_init(void) { - int ret, result; + int ret, result, max_brightness; if (acpi_disabled) return -ENODEV; @@ -265,6 +575,11 @@ static int __init fujitsu_init(void) return -ENOMEM; memset(fujitsu, 0, sizeof(struct fujitsu_t)); + if (use_alt_lcd_levels==-1) + fujitsu->usealt = dmi_check_system(fujitsu_dmi_table); + else + fujitsu->usealt = use_alt_lcd_levels != 0 ? 1 : 0; + result = acpi_bus_register_driver(&acpi_fujitsu_driver); if (result < 0) { ret = -ENODEV; @@ -273,13 +588,22 @@ static int __init fujitsu_init(void) /* 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; @@ -322,6 +646,7 @@ static int __init fujitsu_init(void) fail_backlight: backlight_device_unregister(fujitsu->bl_device); + acpi_bus_unregister_driver(&acpi_fujitsu_driver); fail_acpi: @@ -348,6 +673,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); ^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH] ACPI: Support Fujitsu Lifebook S6410 in fujitsu-laptop 2008-04-15 9:36 ` nokos @ 2008-04-15 23:12 ` Jonathan Woithe 0 siblings, 0 replies; 17+ messages in thread From: Jonathan Woithe @ 2008-04-15 23:12 UTC (permalink / raw) To: nokos; +Cc: linux-acpi, Jonathan Woithe > Here is a modularized version of the patch, it also includes autodetection > of S6410 to switch between the brightness interface (can be overridden by > module parameter) > > Jonathan can you test it if it doesn't break anything on the S7020? Will do. A comment on the patch - there seems to be a fair number of seemingly cosmetic and unnecessary whitespace/ordering changes. Perhaps try to minimise these as it makes the true impact of the patch much clearer and thus easier to evaluate and work with. For example: > diff --git a/drivers/misc/fujitsu-laptop.c b/drivers/misc/fujitsu-laptop.c > index e2e7c05..9a80d29 100644 > --- a/drivers/misc/fujitsu-laptop.c > +++ b/drivers/misc/fujitsu-laptop.c > @@ -2,6 +2,7 @@ : > @@ -56,21 +65,36 @@ > #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" > +#define ACPI_FUJITSUB1_HID "FUJ02B1" > +#define ACPI_FUJITSUB1_DEVICE_NAME "Fujitsu FUJ02B1" As far as I can tell all you've really changed here is ACPI_FUJITSU_DRIVER_NAME, yet ACPI_FUJITSUB1_HID and ACPI_FUJITSUB1_DEVICE_NAME are also flagged as changed. Another one: > status = > - acpi_evaluate_integer(fujitsu->acpi_handle, "GBLL", NULL, &state); > + acpi_evaluate_integer(fujitsu->acpi_handle, "GBLL", NULL, > + &state); Even though the acpi_evaluate_integer() call hasn't been altered it appears in the patch due to a minor and unneeded formatting change. Regards jonathan ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH] ACPI: Support Fujitsu Lifebook S6410 in fujitsu-laptop 2008-04-14 10:46 ` Thomas Renninger ` (2 preceding siblings ...) [not found] ` <1208193500.11305.17.camel@earth.gruber.myown> @ 2008-04-15 3:11 ` Jonathan Woithe 3 siblings, 0 replies; 17+ messages in thread From: Jonathan Woithe @ 2008-04-15 3:11 UTC (permalink / raw) To: trenn; +Cc: Jonathan Woithe, nokos, linux-acpi Hi > IMO there should be two fujitsu drivers, one for: > ACPI_FUJITSUB1_HID "FUJ02B1" > e.g. acpi_fujitsu_brightness > or > acpi_fujitsu_02b1 > and one for: > ACPI_FUJITSUE3_HID "FUJ02E3" > e.g. acpi_fujitsu_keys > or > acpi_fujitsu_02e3 > not sure what the perfect name could be... > > There probably are machines out there which only have one of above > devices, then only the relevant driver should be loaded. I tend to agree with the idea of separate drivers since circumstantial evidence suggests that Fujitsu do change the roles of these components between models. It think it would be entirely possible that some laptops have only one of these device functions. Given this, the modular approach would seem to be more maintainable going forward. I haven't however looked into this in any detail. As for names, "acpi_fujitsu_brightness", "acpi_fujitsu_keys" makes sense but are perhaps a little long. Going with just "fujitsu_brightness" and "fujitsu_keys" isn't really any less clear unless of course the respective devices end up controlling things other than those already identified. To stay completely generic, "fujitsu_02b1" and "fujitsu_02e3" would be best although clearly these don't suggest any idea of their functionality. > You should also take people into CC who are already stated as authors in > the driver, chances are high that they miss this post. Thanks for raising this. I did indeed miss it. :-) > Someone should review this who is deeper involved into fujitsu machines. I wouldn't say I'm deepling involved in the fujitsu machines or all that knowledgeable about ACPI, but I do have an S7020 which is what the fujitsu-laptop module was developed on. :-) Regards jonathan ^ permalink raw reply [flat|nested] 17+ messages in thread
[parent not found: <1208366478.9921.13.camel@earth.gruber.myown>]
* Re: [PATCH] ACPI: Support Fujitsu Lifebook S6410 in fujitsu-laptop [not found] <1208366478.9921.13.camel@earth.gruber.myown> @ 2008-04-21 0:24 ` Jonathan Woithe 0 siblings, 0 replies; 17+ messages in thread From: Jonathan Woithe @ 2008-04-21 0:24 UTC (permalink / raw) To: nokos; +Cc: Jonathan Woithe, linux-acpi Hi Peter > ok here is a "not run through indent" version which minimizes the > whitespace changes. Hope this clears up the patch. Sorry for the delay. I have now tested your patch with the S7020 laptop and made the following observations. 1) The patches to fujitsu-laptop do not appear to have broken software control of the brightness on the S7020 - this continued to work as expected. 2) The changes to fujitsu-laptop now mean that the brightness keys on the keyboard generate APCI button press events (not that I use them, but others might). Note that on the S7020 the brightness keys function independently of the operating system. The only advantage in seeing the keypress events is probably so brightness applets (if running) get to know about brightness changes. 3) I forced the use of the "alternative" brightness interface to see what happens on the S7020. It doesn't work. Therefore the hooks used by the alternative interface definitely don't exist on the S7020 and the two methods of brightness control will probably have to stay (since if I recall you mentioned the original method didn't work on the S6410). 4) The S7020 has a series of 5 "custom" buttons labelled 1, 2, 3, 4 and "enter" at the top of the keyboard. These are, as I understand it, used to input a security code if thus configured. With fujitsu-laptop-hotkeys loaded these keys were observed to generate ACPI keypress events which were seen by xev. However, there seemed to be some kind of FIFO in action - if I pressed a key it would only appear after 4 other keypresses had been made. For example: Button sequence: 1, 2, 3, 4, 3, 2, ... ACPI response: nothing, nothing, nothing, nothing, 1, 2, ... Any thoughts? Is this just a delay through the Linux ACPI kernel/daemon system? I don't think so because the brightness key notifications come through almost instantly. 5) The loading of the fujitsu-laptop-hotkeys did not appear to cause any breakage of other fujitsu-laptop functions. I'm particularly interested to know whether you have any thoughts regarding (4) but for me this isn't a deal-breaker since I have no real use for these buttons. In a way the fact that fujitsu-laptop-hotkeys doesn't seem to work reliably on the S7020 is probably an argument for keeping it separate from fujitsu-laptop. A few other comments regarding the patch. If fujitsu-laptop-hotkeys remains as a standalone module (which is what I'm leaning towards at present) then for the sake of clarity the namespace in fujitsu-laptop-hotkeys should be cleaned up so it doesn't clash with that in fujitsu-laptop. The clash isn't technically relevant because no exported names are affected, but to prevent confusion in future I think it would be good to have separate names. I suggest something like FUJITSU_DRIVER_VERSION -> FUJITSU_HOTKEYS_DRIVER_VERSION fujitsu_t -> fujitsu_hotkeys_t acpi_fujitsu_*() -> acpi_fujitsu_hotkeys_*() The version number of fujitsu-laptop should be incremented to 0.4 by this patch. I also have to work out whether fujitsu-laptop-hotkeys will take its version number from fujitsu-laptop or maintain its own version number. I'm in two minds about this and want to think it over some more. Regards jonathan ^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2008-04-21 0:25 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-03-05 3:01 [PATCH] ACPI: Support Fujitsu Lifebook S6410 in fujitsu-laptop nokos
2008-04-09 23:40 ` nokos
2008-04-13 16:22 ` nokos
2008-04-14 10:46 ` Thomas Renninger
2008-04-14 11:19 ` nokos
2008-04-14 12:11 ` Thomas Renninger
2008-04-15 3:14 ` Jonathan Woithe
2008-04-15 10:51 ` nokos
2008-04-15 23:21 ` Jonathan Woithe
2008-04-14 18:00 ` nokos
[not found] ` <1208193500.11305.17.camel@earth.gruber.myown>
[not found] ` <1208195761.1784.95.camel@queen.suse.de>
2008-04-14 18:22 ` nokos
2008-04-14 19:03 ` Thomas Renninger
2008-04-14 19:18 ` Thomas Renninger
2008-04-15 9:36 ` nokos
2008-04-15 23:12 ` Jonathan Woithe
2008-04-15 3:11 ` Jonathan Woithe
[not found] <1208366478.9921.13.camel@earth.gruber.myown>
2008-04-21 0:24 ` Jonathan Woithe
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox