/* Copyright (C) 2007,2008 Jonathan Woithe <jwoithe@just42.net> Copyright (C) 2008 Peter Gruber <nokos@gmx.net> Copyright (C) 2008 Tony Vroon <tony@linx.net> Based on earlier work: Copyright (C) 2003 Shane Spencer <shane@bogomip.com> Adrian Yee <brewt-fujitsu@brewt.org>
Templated from msi-laptop.c and thinkpad_acpi.c which is copyright by its respective authors.
*/
/* * fujitsu-laptop.c - Fujitsu laptop support, providing access to additional * features made available on a range of Fujitsu laptops including the * P2xxx/P5xxx/S2xxx/S6xxx/S7xxx series. * * This driver implements a vendor-specific backlight control interface for * Fujitsu laptops and provides support for hotkeys present on certain Fujitsu * laptops. * * This driver has been tested on a Fujitsu Lifebook S2110, S6410, S7020 and * P8010. It should work on most P-series and S-series Lifebooks, but * YMMV. * * The module parameter use_alt_lcd_levels switches between different ACPI * brightness controls which are used by different Fujitsu laptops. In most * cases the correct method is automatically detected. "use_alt_lcd_levels=1" * is applicable for a Fujitsu Lifebook S6410 if autodetection fails. *
*/
/* Device used to access hotkeys and other features on the laptop */ struct fujitsu_laptop { struct input_dev *input; char phys[32]; struct platform_device *pf_device; struct kfifo fifo;
spinlock_t fifo_lock; int flags_supported; int flags_state; bool charge_control_supported;
};
staticstruct acpi_device *fext;
/* Fujitsu ACPI interface function */
staticint call_fext_func(struct acpi_device *device, int func, int op, int feature, int state)
{ union acpi_object params[4] = {
{ .integer.type = ACPI_TYPE_INTEGER, .integer.value = func },
{ .integer.type = ACPI_TYPE_INTEGER, .integer.value = op },
{ .integer.type = ACPI_TYPE_INTEGER, .integer.value = feature },
{ .integer.type = ACPI_TYPE_INTEGER, .integer.value = state }
}; struct acpi_object_list arg_list = { 4, params }; unsignedlonglong value;
acpi_status status;
status = acpi_evaluate_integer(device->handle, "FUNC", &arg_list,
&value); if (ACPI_FAILURE(status)) {
acpi_handle_err(device->handle, "Failed to evaluate FUNC\n"); return -ENODEV;
}
/* Battery charge control code */ static ssize_t charge_control_end_threshold_store(struct device *dev, struct device_attribute *attr, constchar *buf, size_t count)
{ int cc_end_value, s006_cc_return; unsignedint value; int ret;
ret = kstrtouint(buf, 10, &value); if (ret) return ret;
if (value > 100) return -EINVAL;
if (value < 50)
value = 50;
cc_end_value = value * 0x100 + 0x20;
s006_cc_return = call_fext_func(fext, FUNC_S006_METHOD,
CHARGE_CONTROL_RW, cc_end_value, 0x0); if (s006_cc_return < 0) return s006_cc_return; /* * The S006 0x21 method returns 0x00 in case the provided value * is invalid.
*/ if (s006_cc_return == 0x00) return -EINVAL;
/* * These functions are intended to be called from acpi_fujitsu_laptop_add and * acpi_fujitsu_laptop_remove.
*/ staticint fujitsu_battery_charge_control_add(struct acpi_device *device)
{ struct fujitsu_laptop *priv = acpi_driver_data(device); int s006_cc_return;
priv->charge_control_supported = false; /* * Check if the S006 0x21 method exists by trying to get the current * battery charge limit.
*/
s006_cc_return = call_fext_func(fext, FUNC_S006_METHOD,
CHARGE_CONTROL_RW, 0x21, 0x0); if (s006_cc_return < 0) return s006_cc_return; if (s006_cc_return == UNSUPPORTED_CMD) return -ENODEV;
if (call_fext_func(device,
FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) {
led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL); if (!led) return -ENOMEM;
led->name = "fujitsu::logolamp";
led->brightness_set_blocking = logolamp_set;
led->brightness_get = logolamp_get;
ret = devm_led_classdev_register(&device->dev, led); if (ret) return ret;
}
if ((call_fext_func(device,
FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) &&
(call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) {
led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL); if (!led) return -ENOMEM;
led->name = "fujitsu::kblamps";
led->brightness_set_blocking = kblamps_set;
led->brightness_get = kblamps_get;
ret = devm_led_classdev_register(&device->dev, led); if (ret) return ret;
}
/* * Some Fujitsu laptops have a radio toggle button in place of a slide * switch and all such machines appear to also have an RF LED. Based on * comparing DSDT tables of four Fujitsu Lifebook models (E744, E751, * S7110, S8420; the first one has a radio toggle button, the other * three have slide switches), bit 17 of flags_supported (the value * returned by method S000 of ACPI device FUJ02E3) seems to indicate * whether given model has a radio toggle button.
*/ if (priv->flags_supported & BIT(17)) {
led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL); if (!led) return -ENOMEM;
/* Support for eco led is not always signaled in bit corresponding * to the bit used to control the led. According to the DSDT table, * bit 14 seems to indicate presence of said led as well. * Confirm by testing the status.
*/ if ((call_fext_func(device, FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) &&
(call_fext_func(device,
FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) {
led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL); if (!led) return -ENOMEM;
led->name = "fujitsu::eco_led";
led->brightness_set_blocking = eco_led_set;
led->brightness_get = eco_led_get;
ret = devm_led_classdev_register(&device->dev, led); if (ret) return ret;
}
return 0;
}
staticint acpi_fujitsu_laptop_add(struct acpi_device *device)
{ struct fujitsu_laptop *priv; int ret, i = 0;
priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM;
WARN_ONCE(fext, "More than one FUJ02E3 ACPI device was found. Driver may not work as intended.");
fext = device;
/* Make sure our bitmask of supported functions is cleared if the
RFKILL function block is not implemented, like on the S7020. */ if (priv->flags_supported == UNSUPPORTED_CMD)
priv->flags_supported = 0;
if (priv->flags_supported)
priv->flags_state = call_fext_func(device, FUNC_FLAGS, 0x4, 0x0,
0x0);
/* Suspect this is a keymap of the application panel, print it */
acpi_handle_info(device->handle, "BTNI: [0x%x]\n",
call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0));
/* Sync backlight power status */ if (fujitsu_bl && fujitsu_bl->bl_device &&
acpi_video_get_backlight_type() == acpi_backlight_vendor) { if (call_fext_func(fext, FUNC_BACKLIGHT, 0x2,
BACKLIGHT_PARAM_POWER, 0x0) == BACKLIGHT_OFF)
fujitsu_bl->bl_device->props.power = BACKLIGHT_POWER_OFF; else
fujitsu_bl->bl_device->props.power = BACKLIGHT_POWER_ON;
}
ret = acpi_fujitsu_laptop_input_setup(device); if (ret) goto err_free_fifo;
ret = acpi_fujitsu_laptop_leds_register(device); if (ret) goto err_free_fifo;
ret = fujitsu_laptop_platform_add(device); if (ret) goto err_free_fifo;
ret = fujitsu_battery_charge_control_add(device); if (ret < 0)
pr_warn("Unable to register battery charge control: %d\n", ret);
/* * First seen on the Skylake-based Lifebook E736/E746/E756), the * touchpad toggle hotkey (Fn+F4) is handled in software. Other models * have since added additional "soft keys". These are reported in the * status flags queried using FUNC_FLAGS.
*/ if (priv->flags_supported & (FLAG_SOFTKEYS)) {
flags = call_fext_func(device, FUNC_FLAGS, 0x1, 0x0, 0x0);
flags &= (FLAG_SOFTKEYS);
for_each_set_bit(i, &flags, BITS_PER_LONG)
sparse_keymap_report_event(priv->input, BIT(i), 1, true);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.