// SPDX-License-Identifier: GPL-2.0-or-later /* * toshiba_acpi.c - Toshiba Laptop ACPI Extras * * Copyright (C) 2002-2004 John Belmonte * Copyright (C) 2008 Philip Langdale * Copyright (C) 2010 Pierre Ducroquet * Copyright (C) 2014-2016 Azael Avalos * * The devolpment page for this driver is located at * http://memebeam.org/toys/ToshibaAcpiDriver. * * Credits: * Jonathan A. Buzzard - Toshiba HCI info, and critical tips on reverse * engineering the Windows drivers * Yasushi Nagato - changes for linux kernel 2.4 -> 2.5 * Rob Miller - TV out and hotkeys help
*/
/* * The Toshiba configuration interface is composed of the HCI and the SCI, * which are defined as follows: * * HCI is Toshiba's "Hardware Control Interface" which is supposed to * be uniform across all their models. Ideally we would just call * dedicated ACPI methods instead of using this primitive interface. * However the ACPI methods seem to be incomplete in some areas (for * example they allow setting, but not reading, the LCD brightness value), * so this is still useful. * * SCI stands for "System Configuration Interface" which aim is to * conceal differences in hardware between different models.
*/
int force_fan; int last_key_event; int key_event_valid; int kbd_type; int kbd_mode; int kbd_time; int usbsc_bat_level; int usbsc_mode_base; int hotkey_event_type; int max_cooling_method;
if (ACPI_FAILURE(status)) {
pr_err("ACPI call to open SCI failed\n"); return 0;
}
if (out[0] == TOS_OPEN_CLOSE_OK) { return 1;
} elseif (out[0] == TOS_ALREADY_OPEN) {
pr_info("Toshiba SCI already opened\n"); return 1;
} elseif (out[0] == TOS_NOT_SUPPORTED) { /* * Some BIOSes do not have the SCI open/close functions * implemented and return 0x8000 (Not Supported), failing to * register some supported features. * * Simply return 1 if we hit those affected laptops to make the * supported features work. * * In the case that some laptops really do not support the SCI, * all the SCI dependent functions check for TOS_NOT_SUPPORTED, * and thus, not registering support for the queried feature.
*/ return 1;
} elseif (out[0] == TOS_NOT_PRESENT) {
pr_info("Toshiba SCI is not present\n");
}
status = tci_raw(dev, in, out);
sci_close(dev); if (ACPI_FAILURE(status)) {
pr_err("ACPI call to query kbd illumination support failed\n"); return;
}
if (out[0] != TOS_SUCCESS) return;
/* * Check for keyboard backlight timeout max value, * previous kbd backlight implementation set this to * 0x3c0003, and now the new implementation set this * to 0x3c001a, use this to distinguish between them.
*/ if (out[3] == SCI_KBD_TIME_MAX)
dev->kbd_type = 2; else
dev->kbd_type = 1; /* Get the current keyboard backlight mode */
dev->kbd_mode = out[2] & SCI_KBD_MODE_MASK; /* Get the current time (1-60 seconds) */
dev->kbd_time = out[2] >> HCI_MISC_SHIFT; /* Flag as supported */
dev->kbd_illum_supported = 1;
}
result = sci_write(dev, SCI_KBD_ILLUM_STATUS, time);
sci_close(dev); if (result == TOS_FAILURE)
pr_err("ACPI call to set KBD backlight status failed\n"); elseif (result == TOS_NOT_SUPPORTED) return -ENODEV;
result = sci_read(dev, SCI_KBD_ILLUM_STATUS, time);
sci_close(dev); if (result == TOS_FAILURE)
pr_err("ACPI call to get KBD backlight status failed\n"); elseif (result == TOS_NOT_SUPPORTED) return -ENODEV;
/* Set the keyboard backlight state */
state = brightness ? 1 : 0;
result = hci_write(dev, HCI_KBD_ILLUMINATION, state); if (result == TOS_FAILURE)
pr_err("ACPI call to set KBD Illumination mode failed\n");
}
result = sci_write(dev, SCI_TOUCHPAD, state);
sci_close(dev); if (result == TOS_FAILURE)
pr_err("ACPI call to set the touchpad failed\n"); elseif (result == TOS_NOT_SUPPORTED) return -ENODEV;
status = tci_raw(dev, in, out); if (ACPI_FAILURE(status)) {
pr_err("ACPI call to get ECO led failed\n"); return;
}
if (out[0] == TOS_INPUT_DATA_ERROR || out[0] == TOS_NOT_SUPPORTED) { /* * If we receive 0x8300 (Input Data Error), it means that the * LED device is present, but that we just screwed the input * parameters. * * On some laptops 0x8000 (Not supported) is also returned in * this case, so we need to allow for that as well. * * Let's query the status of the LED to see if we really have a * success response, indicating the actual presense of the LED, * bail out otherwise.
*/
in[3] = 1;
status = tci_raw(dev, in, out); if (ACPI_FAILURE(status)) {
pr_err("ACPI call to get ECO led failed\n"); return;
}
/* Switch the Eco Mode led on/off */
in[2] = (brightness) ? 1 : 0;
status = tci_raw(dev, in, out); if (ACPI_FAILURE(status))
pr_err("ACPI call to set ECO led failed\n");
}
/* * Check if the accelerometer call exists, * this call also serves as initialization
*/
status = tci_raw(dev, in, out); if (ACPI_FAILURE(status)) {
pr_err("ACPI call to query the accelerometer failed\n"); return;
}
/* Check the Accelerometer status */
status = tci_raw(dev, in, out); if (ACPI_FAILURE(status)) {
pr_err("ACPI call to query the accelerometer failed\n"); return -EIO;
}
status = tci_raw(dev, in, out); if (ACPI_FAILURE(status)) {
pr_err("ACPI call to get USB Sleep and Charge mode failed\n");
sci_close(dev); return;
}
if (out[0] != TOS_SUCCESS) {
sci_close(dev); return;
}
dev->usbsc_mode_base = out[4];
in[5] = SCI_USB_CHARGE_BAT_LVL;
status = tci_raw(dev, in, out);
sci_close(dev); if (ACPI_FAILURE(status)) {
pr_err("ACPI call to get USB Sleep and Charge mode failed\n"); return;
}
if (out[0] != TOS_SUCCESS) return;
dev->usbsc_bat_level = out[2]; /* Flag as supported */
dev->usb_sleep_charge_supported = 1;
}
result = sci_read(dev, SCI_USB_SLEEP_CHARGE, mode);
sci_close(dev); if (result == TOS_FAILURE)
pr_err("ACPI call to set USB S&C mode failed\n"); elseif (result == TOS_NOT_SUPPORTED) return -ENODEV;
result = sci_write(dev, SCI_USB_SLEEP_CHARGE, mode);
sci_close(dev); if (result == TOS_FAILURE)
pr_err("ACPI call to set USB S&C mode failed\n"); elseif (result == TOS_NOT_SUPPORTED) return -ENODEV;
in[5] = SCI_USB_CHARGE_BAT_LVL;
status = tci_raw(dev, in, out);
sci_close(dev); if (ACPI_FAILURE(status)) {
pr_err("ACPI call to get USB S&C battery level failed\n"); return -EIO;
}
in[2] = mode;
in[5] = SCI_USB_CHARGE_BAT_LVL;
status = tci_raw(dev, in, out);
sci_close(dev); if (ACPI_FAILURE(status)) {
pr_err("ACPI call to set USB S&C battery level failed\n"); return -EIO;
}
in[5] = SCI_USB_CHARGE_RAPID_DSP;
status = tci_raw(dev, in, out);
sci_close(dev); if (ACPI_FAILURE(status)) {
pr_err("ACPI call to get USB Rapid Charge failed\n"); return -EIO;
}
if (out[0] == TOS_NOT_SUPPORTED) return -ENODEV;
if (out[0] != TOS_SUCCESS && out[0] != TOS_SUCCESS2) return -EIO;
in[2] = state;
in[5] = SCI_USB_CHARGE_RAPID_DSP;
status = tci_raw(dev, in, out);
sci_close(dev); if (ACPI_FAILURE(status)) {
pr_err("ACPI call to set USB Rapid Charge failed\n"); return -EIO;
}
result = sci_read(dev, SCI_USB_SLEEP_MUSIC, state);
sci_close(dev); if (result == TOS_FAILURE)
pr_err("ACPI call to get Sleep and Music failed\n"); elseif (result == TOS_NOT_SUPPORTED) return -ENODEV;
result = sci_write(dev, SCI_USB_SLEEP_MUSIC, state);
sci_close(dev); if (result == TOS_FAILURE)
pr_err("ACPI call to set Sleep and Music failed\n"); elseif (result == TOS_NOT_SUPPORTED) return -ENODEV;
result = sci_read(dev, SCI_KBD_FUNCTION_KEYS, mode);
sci_close(dev); if (result == TOS_FAILURE)
pr_err("ACPI call to get KBD function keys failed\n"); elseif (result == TOS_NOT_SUPPORTED) return -ENODEV;
result = sci_write(dev, SCI_KBD_FUNCTION_KEYS, mode);
sci_close(dev); if (result == TOS_FAILURE)
pr_err("ACPI call to set KBD function keys failed\n"); elseif (result == TOS_NOT_SUPPORTED) return -ENODEV;
/* Panel Power ON */ staticint toshiba_panel_power_on_get(struct toshiba_acpi_dev *dev, u32 *state)
{
u32 result;
if (!sci_open(dev)) return -EIO;
result = sci_read(dev, SCI_PANEL_POWER_ON, state);
sci_close(dev); if (result == TOS_FAILURE)
pr_err("ACPI call to get Panel Power ON failed\n"); elseif (result == TOS_NOT_SUPPORTED) return -ENODEV;
result = sci_write(dev, SCI_PANEL_POWER_ON, state);
sci_close(dev); if (result == TOS_FAILURE)
pr_err("ACPI call to set Panel Power ON failed\n"); elseif (result == TOS_NOT_SUPPORTED) return -ENODEV;
return result == TOS_SUCCESS ? 0 : -EIO;
}
/* USB Three */ staticint toshiba_usb_three_get(struct toshiba_acpi_dev *dev, u32 *state)
{
u32 result;
if (!sci_open(dev)) return -EIO;
result = sci_read(dev, SCI_USB_THREE, state);
sci_close(dev); if (result == TOS_FAILURE)
pr_err("ACPI call to get USB 3 failed\n"); elseif (result == TOS_NOT_SUPPORTED) return -ENODEV;
result = sci_write(dev, SCI_USB_THREE, state);
sci_close(dev); if (result == TOS_FAILURE)
pr_err("ACPI call to set USB 3 failed\n"); elseif (result == TOS_NOT_SUPPORTED) return -ENODEV;
/* * WWAN support can be queried by setting the in[3] value to * HCI_WIRELESS_WWAN (0x03). * * If supported, out[0] contains TOS_SUCCESS and out[2] contains * HCI_WIRELESS_WWAN_STATUS (0x2000). * * If not supported, out[0] contains TOS_INPUT_DATA_ERROR (0x8300) * or TOS_NOT_SUPPORTED (0x8000).
*/
in[3] = HCI_WIRELESS_WWAN;
status = tci_raw(dev, in, out); if (ACPI_FAILURE(status)) {
pr_err("ACPI call to get WWAN status failed\n"); return;
}
in[3] = HCI_WIRELESS_WWAN_STATUS;
status = tci_raw(dev, in, out); if (ACPI_FAILURE(status)) {
pr_err("ACPI call to set WWAN status failed\n"); return -EIO;
}
if (out[0] == TOS_NOT_SUPPORTED) return -ENODEV;
if (out[0] != TOS_SUCCESS) return -EIO;
/* * Some devices only need to call HCI_WIRELESS_WWAN_STATUS to * (de)activate the device, but some others need the * HCI_WIRELESS_WWAN_POWER call as well.
*/
in[3] = HCI_WIRELESS_WWAN_POWER;
status = tci_raw(dev, in, out); if (ACPI_FAILURE(status)) {
pr_err("ACPI call to set WWAN power failed\n"); return -EIO;
}
if (ACPI_FAILURE(status))
pr_err("ACPI call to get Battery Charge Mode failed\n"); switch (out[0]) { case TOS_SUCCESS: case TOS_SUCCESS2:
*state = out[2]; return 0; case TOS_NOT_SUPPORTED: return -ENODEV; case TOS_DATA_NOT_AVAILABLE:
retries--; break; default: return -EIO;
}
} while (retries);
staticint set_lcd_brightness(struct toshiba_acpi_dev *dev, int value)
{
u32 result;
if (dev->tr_backlight_supported) { int ret = set_tr_backlight_status(dev, !value);
if (ret) return ret; if (value)
value--;
}
value = value << HCI_LCD_BRIGHTNESS_SHIFT;
result = hci_write(dev, HCI_LCD_BRIGHTNESS, value); if (result == TOS_FAILURE)
pr_err("ACPI call to set LCD Brightness failed\n"); elseif (result == TOS_NOT_SUPPORTED) return -ENODEV;
cmd = memdup_user_nul(buf, count); if (IS_ERR(cmd)) return PTR_ERR(cmd);
buffer = cmd;
/* * Scan expression. Multiple expressions may be delimited with ; * NOTE: To keep scanning simple, invalid fields are ignored.
*/ while (remain) { if (sscanf(buffer, " lcd_out : %i", &value) == 1)
lcd_out = value & 1; elseif (sscanf(buffer, " crt_out : %i", &value) == 1)
crt_out = value & 1; elseif (sscanf(buffer, " tv_out : %i", &value) == 1)
tv_out = value & 1; /* Advance to one character past the next ; */ do {
++buffer;
--remain;
} while (remain && *(buffer - 1) != ';');
}
kfree(cmd);
ret = get_video_status(dev, &video_out); if (!ret) { unsignedint new_video_out = video_out;
if (lcd_out != -1)
_set_bit(&new_video_out, HCI_VIDEO_OUT_LCD, lcd_out); if (crt_out != -1)
_set_bit(&new_video_out, HCI_VIDEO_OUT_CRT, crt_out); if (tv_out != -1)
_set_bit(&new_video_out, HCI_VIDEO_OUT_TV, tv_out); /* * To avoid unnecessary video disruption, only write the new * video setting if something changed.
*/ if (new_video_out != video_out)
ret = write_acpi_int(METHOD_VIDEO_OUT, new_video_out);
}
ret = kstrtoint(buf, 0, &mode); if (ret) return ret;
/* Check for supported modes depending on keyboard backlight type */ if (toshiba->kbd_type == 1) { /* Type 1 supports SCI_KBD_MODE_FNZ and SCI_KBD_MODE_AUTO */ if (mode != SCI_KBD_MODE_FNZ && mode != SCI_KBD_MODE_AUTO) return -EINVAL;
} elseif (toshiba->kbd_type == 2) { /* Type 2 doesn't support SCI_KBD_MODE_FNZ */ if (mode != SCI_KBD_MODE_AUTO && mode != SCI_KBD_MODE_ON &&
mode != SCI_KBD_MODE_OFF) return -EINVAL;
}
/* * Set the Keyboard Backlight Mode where: * Auto - KBD backlight turns off automatically in given time * FN-Z - KBD backlight "toggles" when hotkey pressed * ON - KBD backlight is always on * OFF - KBD backlight is always off
*/
/* Only make a change if the actual mode has changed */ if (toshiba->kbd_mode != mode) { /* Shift the time to "base time" (0x3c0000 == 60 seconds) */ int time = toshiba->kbd_time << HCI_MISC_SHIFT;
/* OR the "base time" to the actual method format */ if (toshiba->kbd_type == 1) { /* Type 1 requires the current mode */
time |= toshiba->kbd_mode;
} elseif (toshiba->kbd_type == 2) { /* Type 2 requires the desired mode */
time |= mode;
}
ret = toshiba_kbd_illum_status_set(toshiba, time); if (ret) return ret;
/* * Some laptop models with the second generation backlit * keyboard (type 2) do not generate the keyboard backlight * changed event (0x92), and thus, the driver will never update * the sysfs entries. * * The event is generated right when changing the keyboard * backlight mode and the *notify function will set the * kbd_event_generated to true. * * In case the event is not generated, schedule the keyboard * backlight work to update the sysfs entries and emulate the * event via genetlink.
*/ if (toshiba->kbd_type == 2 &&
!toshiba->kbd_event_generated)
schedule_work(&kbd_bl_work);
}
ret = kstrtoint(buf, 0, &time); if (ret) return ret;
/* Check for supported values depending on kbd_type */ if (toshiba->kbd_type == 1) { if (time < 0 || time > 60) return -EINVAL;
} elseif (toshiba->kbd_type == 2) { if (time < 1 || time > 60) return -EINVAL;
}
/* Set the Keyboard Backlight Timeout */
/* Only make a change if the actual timeout has changed */ if (toshiba->kbd_time != time) { /* Shift the time to "base time" (0x3c0000 == 60 seconds) */
time = time << HCI_MISC_SHIFT; /* OR the "base time" to the actual method format */ if (toshiba->kbd_type == 1)
time |= SCI_KBD_MODE_FNZ; elseif (toshiba->kbd_type == 2)
time |= SCI_KBD_MODE_AUTO;
ret = toshiba_kbd_illum_status_set(toshiba, time); if (ret) return ret;
ret = kstrtoint(buf, 0, &mode); if (ret) return ret; /* * Check for the function keys mode where: * 0 - Normal operation (F{1-12} as usual and hotkeys via FN-F{1-12}) * 1 - Special functions (Opposite of the above setting)
*/ if (mode != 0 && mode != 1) return -EINVAL;
ret = toshiba_function_keys_set(toshiba, mode); if (ret) return ret;
pr_info("Reboot for changes to KBD Function Keys to take effect");
ret = kstrtoint(buf, 0, &state); if (ret) return ret; /* * Check for USB 3 mode where: * 0 - Disabled (Acts like a USB 2 port, saving power) * 1 - Enabled
*/ if (state != 0 && state != 1) return -EINVAL;
ret = toshiba_usb_three_set(toshiba, state); if (ret) return ret;
pr_info("Reboot for changes to USB 3 to take effect");
return count;
} static DEVICE_ATTR_RW(usb_three);
static ssize_t cooling_method_show(struct device *dev, struct device_attribute *attr, char *buf)
{ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); int state; int ret;
ret = toshiba_cooling_method_get(toshiba, &state); if (ret < 0) return ret;
ret = kstrtoint(buf, 0, &state); if (ret) return ret;
/* * Check for supported values * Depending on the laptop model, some only support these two: * 0 - Maximum Performance * 1 - Battery Optimized * * While some others support all three methods: * 0 - Maximum Performance * 1 - Performance * 2 - Battery Optimized
*/ if (state < 0 || state > toshiba->max_cooling_method) return -EINVAL;
ret = toshiba_cooling_method_set(toshiba, state); if (ret) return ret;
switch (cmd) { case TOSH_SMM: if (copy_from_user(®s, argp, sizeof(SMMRegisters))) return -EFAULT;
ret = toshiba_acpi_smm_bridge(®s); if (ret) return ret; if (copy_to_user(argp, ®s, sizeof(SMMRegisters))) return -EFAULT; break; case TOSHIBA_ACPI_SCI: if (copy_from_user(®s, argp, sizeof(SMMRegisters))) return -EFAULT; /* Ensure we are being called with a SCI_{GET, SET} register */ if (regs.eax != SCI_GET && regs.eax != SCI_SET) return -EINVAL; if (!sci_open(toshiba_acpi)) return -EIO;
ret = toshiba_acpi_smm_bridge(®s);
sci_close(toshiba_acpi); if (ret) return ret; if (copy_to_user(argp, ®s, sizeof(SMMRegisters))) return -EFAULT; break; default: return -EINVAL;
}
status = acpi_evaluate_object(dev->acpi_dev->handle, "ENAB", NULL, NULL); if (ACPI_FAILURE(status)) return -ENODEV;
/* * Enable quickstart buttons if supported. * * Enable the "Special Functions" mode only if they are * supported and if they are activated.
*/ if (hci_hotkey_quickstart)
result = hci_write(dev, HCI_HOTKEY_EVENT,
HCI_HOTKEY_ENABLE_QUICKSTART); elseif (dev->kbd_function_keys_supported && dev->special_functions)
result = hci_write(dev, HCI_HOTKEY_EVENT,
HCI_HOTKEY_SPECIAL_FUNCTIONS); else
result = hci_write(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE);
do {
result = hci_read(dev, HCI_SYSTEM_EVENT, &value); switch (result) { case TOS_SUCCESS:
toshiba_acpi_report_hotkey(dev, (int)value);
dev->key_event_valid = 1;
dev->last_key_event = value; break; case TOS_NOT_SUPPORTED: /* * This is a workaround for an unresolved * issue on some machines where system events * sporadically become disabled.
*/
result = hci_write(dev, HCI_SYSTEM_EVENT, 1); if (result == TOS_SUCCESS)
pr_notice("Re-enabled hotkeys\n");
fallthrough; default:
retries--; break;
}
} while (retries && result != TOS_FIFO_EMPTY);
}
}
if (dev->hotkey_event_type == HCI_SYSTEM_TYPE1 ||
!dev->kbd_function_keys_supported)
keymap = toshiba_acpi_keymap; elseif (dev->hotkey_event_type == HCI_SYSTEM_TYPE2 ||
dev->kbd_function_keys_supported)
keymap = toshiba_acpi_alt_keymap; else
pr_info("Unknown event type received %x\n",
dev->hotkey_event_type);
error = sparse_keymap_setup(dev->hotkey_dev, keymap, NULL); if (error) goto err_free_dev;
/* * For some machines the SCI responsible for providing hotkey * notification doesn't fire. We can trigger the notification * whenever the Fn key is pressed using the NTFY method, if * supported, so if it's present set up an i8042 key filter * for this purpose.
*/
ec_handle = ec_get_handle(); if (ec_handle && acpi_has_method(ec_handle, "NTFY")) {
INIT_WORK(&dev->hotkey_work, toshiba_acpi_hotkey_work);
/* * Determine hotkey query interface. Prefer using the INFO * method when it is available.
*/ if (acpi_has_method(dev->acpi_dev->handle, "INFO"))
dev->info_supported = 1; elseif (hci_write(dev, HCI_SYSTEM_EVENT, 1) == TOS_SUCCESS)
dev->system_event_supported = 1;
staticint toshiba_acpi_setup_backlight(struct toshiba_acpi_dev *dev)
{ struct backlight_properties props; int brightness; int ret;
/* * Some machines don't support the backlight methods at all, and * others support it read-only. Either of these is pretty useless, * so only register the backlight device if the backlight method * supports both reads and writes.
*/
brightness = __get_lcd_brightness(dev); if (brightness < 0) return 0; /* * If transflective backlight is supported and the brightness is zero * (lowest brightness level), the set_lcd_brightness function will * activate the transflective backlight, making the LCD appear to be * turned off, simply increment the brightness level to avoid that.
*/ if (dev->tr_backlight_supported && brightness == 0)
brightness++;
ret = set_lcd_brightness(dev, brightness); if (ret) {
pr_debug("Backlight method is read-only, disabling backlight support\n"); return 0;
}
if (acpi_video_get_backlight_type() != acpi_backlight_vendor) return 0;
/* HWMON support for fan */ #if IS_ENABLED(CONFIG_HWMON) static umode_t toshiba_acpi_hwmon_is_visible(constvoid *drvdata, enum hwmon_sensor_types type,
u32 attr, int channel)
{ return 0444;
}
staticint toshiba_acpi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{ /* * There is only a single channel and single attribute (for the * fan) at this point. * This can be replaced with more advanced logic in the future, * should the need arise.
*/ if (type == hwmon_fan && channel == 0 && attr == hwmon_fan_input) {
u32 value; int ret;
ret = get_fan_rpm(toshiba_acpi, &value); if (ret) return ret;
if (dev->hotkey_dev)
pr_cont(" hotkeys"); if (dev->backlight_dev)
pr_cont(" backlight"); if (dev->video_supported)
pr_cont(" video-out"); if (dev->fan_supported)
pr_cont(" fan"); if (dev->fan_rpm_supported)
pr_cont(" fan-rpm"); if (dev->tr_backlight_supported)
pr_cont(" transflective-backlight"); if (dev->illumination_supported)
pr_cont(" illumination"); if (dev->kbd_illum_supported)
pr_cont(" keyboard-backlight"); if (dev->touchpad_supported)
pr_cont(" touchpad"); if (dev->eco_supported)
pr_cont(" eco-led"); if (dev->accelerometer_supported)
pr_cont(" accelerometer-axes"); if (dev->usb_sleep_charge_supported)
pr_cont(" usb-sleep-charge"); if (dev->usb_rapid_charge_supported)
pr_cont(" usb-rapid-charge"); if (dev->usb_sleep_music_supported)
pr_cont(" usb-sleep-music"); if (dev->kbd_function_keys_supported)
pr_cont(" special-function-keys"); if (dev->panel_power_on_supported)
pr_cont(" panel-power-on"); if (dev->usb_three_supported)
pr_cont(" usb3"); if (dev->wwan_supported)
pr_cont(" wwan"); if (dev->cooling_method_supported)
pr_cont(" cooling-method"); if (dev->battery_charge_mode_supported)
pr_cont(" battery-charge-mode");
if (dev->wwan_rfk) {
rfkill_unregister(dev->wwan_rfk);
rfkill_destroy(dev->wwan_rfk);
}
if (dev->battery_charge_mode_supported)
battery_hook_unregister(&battery_hook);
if (toshiba_acpi)
toshiba_acpi = NULL;
kfree(dev);
}
staticconstchar *find_hci_method(acpi_handle handle)
{ if (acpi_has_method(handle, "GHCI")) return"GHCI";
if (acpi_has_method(handle, "SPFC")) return"SPFC";
return NULL;
}
/* * Some Toshibas have a broken acpi-video interface for brightness control, * these are quirked in drivers/acpi/video_detect.c to use the GPU native * (/sys/class/backlight/intel_backlight) instead. * But these need a HCI_SET call to actually turn the panel back on at resume, * without this call the screen stays black at resume. * Either HCI_LCD_BRIGHTNESS (used by acpi_video's _BCM) or HCI_PANEL_POWER_ON * works. toshiba_acpi_resume() uses HCI_PANEL_POWER_ON to avoid changing * the configured brightness level.
*/ #define QUIRK_TURN_ON_PANEL_ON_RESUME BIT(0) /* * Some Toshibas use "quickstart" keys. On these, HCI_HOTKEY_EVENT must use * the value HCI_HOTKEY_ENABLE_QUICKSTART.
*/ #define QUIRK_HCI_HOTKEY_QUICKSTART BIT(1)
/* * The "Special Functions" are always supported by the laptops * with the new keyboard layout, query for its presence to help * determine the keymap layout to use.
*/
ret = toshiba_function_keys_get(dev, &dev->special_functions);
dev->kbd_function_keys_supported = !ret;
dev->hotkey_event_type = 0; if (toshiba_acpi_setup_keyboard(dev))
pr_info("Unable to activate hotkeys\n");
/* Determine whether or not BIOS supports transflective backlight */
ret = get_tr_backlight_status(dev, &dummy);
dev->tr_backlight_supported = !ret;
ret = toshiba_acpi_setup_backlight(dev); if (ret) goto error;
toshiba_kbd_illum_available(dev); /* * Only register the LED if KBD illumination is supported * and the keyboard backlight operation mode is set to FN-Z * or we detect a second gen keyboard backlight
*/ if (dev->kbd_illum_supported &&
(dev->kbd_mode == SCI_KBD_MODE_FNZ || dev->kbd_type == 2)) {
dev->kbd_led.name = "toshiba::kbd_backlight";
dev->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
dev->kbd_led.max_brightness = 1;
dev->kbd_led.brightness_set = toshiba_kbd_backlight_set;
dev->kbd_led.brightness_get = toshiba_kbd_backlight_get;
led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led);
}
ret = toshiba_touchpad_get(dev, &dummy);
dev->touchpad_supported = !ret;
toshiba_accelerometer_available(dev); if (dev->accelerometer_supported) {
dev->indio_dev = iio_device_alloc(&acpi_dev->dev, sizeof(*dev)); if (!dev->indio_dev) {
pr_err("Unable to allocate iio device\n"); goto iio_error;
}
ret = iio_device_register(dev->indio_dev); if (ret < 0) {
pr_err("Unable to register iio device\n");
iio_device_free(dev->indio_dev);
}
}
iio_error:
toshiba_usb_sleep_charge_available(dev);
ret = toshiba_usb_rapid_charge_get(dev, &dummy);
dev->usb_rapid_charge_supported = !ret;
ret = toshiba_usb_sleep_music_get(dev, &dummy);
dev->usb_sleep_music_supported = !ret;
ret = toshiba_panel_power_on_get(dev, &dummy);
dev->panel_power_on_supported = !ret;
ret = toshiba_usb_three_get(dev, &dummy);
dev->usb_three_supported = !ret;
ret = get_video_status(dev, &dummy);
dev->video_supported = !ret;
ret = get_fan_status(dev, &dummy);
dev->fan_supported = !ret;
ret = get_fan_rpm(dev, &dummy);
dev->fan_rpm_supported = !ret;
#if IS_ENABLED(CONFIG_HWMON) if (dev->fan_rpm_supported) {
dev->hwmon_device = hwmon_device_register_with_info(
&dev->acpi_dev->dev, "toshiba_acpi_sensors", NULL,
&toshiba_acpi_hwmon_chip_info, NULL); if (IS_ERR(dev->hwmon_device)) {
dev->hwmon_device = NULL;
pr_warn("unable to register hwmon device, skipping\n");
}
} #endif
toshiba_wwan_available(dev); if (dev->wwan_supported)
toshiba_acpi_setup_wwan_rfkill(dev);
toshiba_cooling_method_available(dev);
toshiba_battery_charge_mode_available(dev);
print_supported_features(dev);
ret = sysfs_create_group(&dev->acpi_dev->dev.kobj,
&toshiba_attr_group); if (ret) {
dev->sysfs_created = 0; goto error;
}
dev->sysfs_created = !ret;
create_toshiba_proc_entries(dev);
toshiba_acpi = dev;
/* * As the battery hook relies on the static variable toshiba_acpi being * set, this must be done after toshiba_acpi is assigned.
*/ if (dev->battery_charge_mode_supported)
battery_hook_register(&battery_hook);
switch (event) { case 0x80: /* Hotkeys and some system events */ /* * Machines with this WMI GUID aren't supported due to bugs in * their AML. * * Return silently to avoid triggering a netlink event.
*/ if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID)) return;
toshiba_acpi_process_hotkeys(dev); break; case 0x81: /* Dock events */ case 0x82: case 0x83:
pr_info("Dock event received %x\n", event); break; case 0x88: /* Thermal events */
pr_info("Thermal event received\n"); break; case 0x8f: /* LID closed */ case 0x90: /* LID is closed and Dock has been ejected */ break; case 0x8c: /* SATA power events */ case 0x8b:
pr_info("SATA power event received %x\n", event); break; case 0x92: /* Keyboard backlight mode changed */
dev->kbd_event_generated = true; /* Update sysfs entries */ if (sysfs_update_group(&acpi_dev->dev.kobj,
&toshiba_attr_group))
pr_err("Unable to update sysfs entries\n"); /* Notify LED subsystem about keyboard backlight change */ if (dev->kbd_type == 2 && dev->kbd_mode != SCI_KBD_MODE_AUTO)
led_classdev_notify_brightness_hw_changed(&dev->kbd_led,
(dev->kbd_mode == SCI_KBD_MODE_ON) ?
LED_FULL : LED_OFF); break; case 0x8e: /* Power button pressed */ break; case 0x85: /* Unknown */ case 0x8d: /* Unknown */ case 0x94: /* Unknown */ case 0x95: /* Unknown */ default:
pr_info("Unknown event received %x\n", event); break;
}
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.