diff options
Diffstat (limited to 'drivers/platform/x86/thinkpad_acpi.c')
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 1156 |
1 files changed, 649 insertions, 507 deletions
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 9c632df734bb..82fa6148216c 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -73,6 +73,7 @@ #include <linux/uaccess.h> #include <acpi/battery.h> #include <acpi/video.h> +#include <drm/drm_privacy_screen_driver.h> #include "dual_accel_detect.h" /* ThinkPad CMOS commands */ @@ -157,6 +158,7 @@ enum tpacpi_hkey_event_t { TP_HKEY_EV_VOL_UP = 0x1015, /* Volume up or unmute */ TP_HKEY_EV_VOL_DOWN = 0x1016, /* Volume down or unmute */ TP_HKEY_EV_VOL_MUTE = 0x1017, /* Mixer output mute */ + TP_HKEY_EV_PRIVACYGUARD_TOGGLE = 0x130f, /* Toggle priv.guard on/off */ /* Reasons for waking up from S3/S4 */ TP_HKEY_EV_WKUP_S3_UNDOCK = 0x2304, /* undock requested, S3 */ @@ -332,12 +334,10 @@ static struct { u32 battery_force_primary:1; u32 input_device_registered:1; u32 platform_drv_registered:1; - u32 platform_drv_attrs_registered:1; u32 sensors_pdrv_registered:1; - u32 sensors_pdrv_attrs_registered:1; - u32 sensors_pdev_attrs_registered:1; u32 hotkey_poll_active:1; u32 has_adaptive_kbd:1; + u32 kbd_lang:1; } tp_features; static struct { @@ -983,20 +983,6 @@ static void tpacpi_shutdown_handler(struct platform_device *pdev) } } -static struct platform_driver tpacpi_pdriver = { - .driver = { - .name = TPACPI_DRVR_NAME, - .pm = &tpacpi_pm, - }, - .shutdown = tpacpi_shutdown_handler, -}; - -static struct platform_driver tpacpi_hwmon_pdriver = { - .driver = { - .name = TPACPI_HWMON_DRVR_NAME, - }, -}; - /************************************************************************* * sysfs support helpers */ @@ -1105,15 +1091,6 @@ static int tpacpi_rfk_update_swstate(const struct tpacpi_rfk *tp_rfk) return status; } -/* Query FW and update rfkill sw state for all rfkill switches */ -static void tpacpi_rfk_update_swstate_all(void) -{ - unsigned int i; - - for (i = 0; i < TPACPI_RFK_SW_MAX; i++) - tpacpi_rfk_update_swstate(tpacpi_rfkill_switches[i]); -} - /* * Sync the HW-blocking state of all rfkill switches, * do notice it causes the rfkill core to schedule uevents @@ -1488,53 +1465,6 @@ static ssize_t uwb_emulstate_store(struct device_driver *drv, const char *buf, static DRIVER_ATTR_RW(uwb_emulstate); #endif -/* --------------------------------------------------------------------- */ - -static struct driver_attribute *tpacpi_driver_attributes[] = { - &driver_attr_debug_level, &driver_attr_version, - &driver_attr_interface_version, -}; - -static int __init tpacpi_create_driver_attributes(struct device_driver *drv) -{ - int i, res; - - i = 0; - res = 0; - while (!res && i < ARRAY_SIZE(tpacpi_driver_attributes)) { - res = driver_create_file(drv, tpacpi_driver_attributes[i]); - i++; - } - -#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES - if (!res && dbg_wlswemul) - res = driver_create_file(drv, &driver_attr_wlsw_emulstate); - if (!res && dbg_bluetoothemul) - res = driver_create_file(drv, &driver_attr_bluetooth_emulstate); - if (!res && dbg_wwanemul) - res = driver_create_file(drv, &driver_attr_wwan_emulstate); - if (!res && dbg_uwbemul) - res = driver_create_file(drv, &driver_attr_uwb_emulstate); -#endif - - return res; -} - -static void tpacpi_remove_driver_attributes(struct device_driver *drv) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(tpacpi_driver_attributes); i++) - driver_remove_file(drv, tpacpi_driver_attributes[i]); - -#ifdef THINKPAD_ACPI_DEBUGFACILITIES - driver_remove_file(drv, &driver_attr_wlsw_emulstate); - driver_remove_file(drv, &driver_attr_bluetooth_emulstate); - driver_remove_file(drv, &driver_attr_wwan_emulstate); - driver_remove_file(drv, &driver_attr_uwb_emulstate); -#endif -} - /************************************************************************* * Firmware Data */ @@ -3008,7 +2938,14 @@ static struct attribute *adaptive_kbd_attributes[] = { NULL }; +static umode_t hadaptive_kbd_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + return tp_features.has_adaptive_kbd ? attr->mode : 0; +} + static const struct attribute_group adaptive_kbd_attr_group = { + .is_visible = hadaptive_kbd_attr_is_visible, .attrs = adaptive_kbd_attributes, }; @@ -3024,6 +2961,8 @@ static struct attribute *hotkey_attributes[] = { &dev_attr_hotkey_all_mask.attr, &dev_attr_hotkey_adaptive_all_mask.attr, &dev_attr_hotkey_recommended_mask.attr, + &dev_attr_hotkey_tablet_mode.attr, + &dev_attr_hotkey_radio_sw.attr, #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL &dev_attr_hotkey_source_mask.attr, &dev_attr_hotkey_poll_freq.attr, @@ -3074,9 +3013,6 @@ static void tpacpi_send_radiosw_update(void) if (wlsw == TPACPI_RFK_RADIO_OFF) tpacpi_rfk_update_hwblock_state(true); - /* Sync sw blocking state */ - tpacpi_rfk_update_swstate_all(); - /* Sync hw blocking state last if it is hw-unblocked */ if (wlsw == TPACPI_RFK_RADIO_ON) tpacpi_rfk_update_hwblock_state(false); @@ -3106,8 +3042,6 @@ static void hotkey_exit(void) hotkey_poll_stop_sync(); mutex_unlock(&hotkey_mutex); #endif - sysfs_remove_group(&tpacpi_pdev->dev.kobj, &hotkey_attr_group); - dbg_printk(TPACPI_DBG_EXIT | TPACPI_DBG_HKEY, "restoring original HKEY status and mask\n"); /* yes, there is a bitwise or below, we want the @@ -3446,7 +3380,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) str_supported(tp_features.hotkey)); if (!tp_features.hotkey) - return 1; + return -ENODEV; quirks = tpacpi_check_quirks(tpacpi_hotkey_qtable, ARRAY_SIZE(tpacpi_hotkey_qtable)); @@ -3502,14 +3436,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) */ if (acpi_evalf(hkey_handle, &hotkey_adaptive_all_mask, "MHKA", "dd", 2)) { - if (hotkey_adaptive_all_mask != 0) { + if (hotkey_adaptive_all_mask != 0) tp_features.has_adaptive_kbd = true; - res = sysfs_create_group( - &tpacpi_pdev->dev.kobj, - &adaptive_kbd_attr_group); - if (res) - goto err_exit; - } } else { tp_features.has_adaptive_kbd = false; hotkey_adaptive_all_mask = 0x0U; @@ -3539,7 +3467,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) * the first hotkey_mask_get to return hotkey_orig_mask */ res = hotkey_mask_get(); if (res) - goto err_exit; + return res; hotkey_orig_mask = hotkey_acpi_mask; } else { @@ -3563,9 +3491,6 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) } tabletsw_state = hotkey_init_tablet_mode(); - res = sysfs_create_group(&tpacpi_pdev->dev.kobj, &hotkey_attr_group); - if (res) - goto err_exit; /* Set up key map */ keymap_id = tpacpi_check_quirks(tpacpi_keymap_qtable, @@ -3578,8 +3503,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) TPACPI_HOTKEY_MAP_SIZE, GFP_KERNEL); if (!hotkey_keycode_map) { pr_err("failed to allocate memory for key map\n"); - res = -ENOMEM; - goto err_exit; + return -ENOMEM; } input_set_capability(tpacpi_inputdev, EV_MSC, MSC_SCAN); @@ -3660,12 +3584,6 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) hotkey_poll_setup_safe(true); return 0; - -err_exit: - sysfs_remove_group(&tpacpi_pdev->dev.kobj, &hotkey_attr_group); - sysfs_remove_group(&tpacpi_pdev->dev.kobj, &adaptive_kbd_attr_group); - - return (res < 0) ? res : 1; } /* Thinkpad X1 Carbon support 5 modes including Home mode, Web browser @@ -3798,6 +3716,30 @@ static bool adaptive_keyboard_hotkey_notify_hotkey(unsigned int scancode) } } +static bool hotkey_notify_extended_hotkey(const u32 hkey) +{ + unsigned int scancode; + + switch (hkey) { + case TP_HKEY_EV_PRIVACYGUARD_TOGGLE: + tpacpi_driver_event(hkey); + return true; + } + + /* Extended keycodes start at 0x300 and our offset into the map + * TP_ACPI_HOTKEYSCAN_EXTENDED_START. The calculated scancode + * will be positive, but might not be in the correct range. + */ + scancode = (hkey & 0xfff) - (0x300 - TP_ACPI_HOTKEYSCAN_EXTENDED_START); + if (scancode >= TP_ACPI_HOTKEYSCAN_EXTENDED_START && + scancode < TPACPI_HOTKEY_MAP_LEN) { + tpacpi_input_send_key(scancode); + return true; + } + + return false; +} + static bool hotkey_notify_hotkey(const u32 hkey, bool *send_acpi_ev, bool *ignore_acpi_ev) @@ -3832,17 +3774,7 @@ static bool hotkey_notify_hotkey(const u32 hkey, return adaptive_keyboard_hotkey_notify_hotkey(scancode); case 3: - /* Extended keycodes start at 0x300 and our offset into the map - * TP_ACPI_HOTKEYSCAN_EXTENDED_START. The calculated scancode - * will be positive, but might not be in the correct range. - */ - scancode -= (0x300 - TP_ACPI_HOTKEYSCAN_EXTENDED_START); - if (scancode >= TP_ACPI_HOTKEYSCAN_EXTENDED_START && - scancode < TPACPI_HOTKEY_MAP_LEN) { - tpacpi_input_send_key(scancode); - return true; - } - break; + return hotkey_notify_extended_hotkey(hkey); } return false; @@ -4396,7 +4328,14 @@ static struct attribute *bluetooth_attributes[] = { NULL }; +static umode_t bluetooth_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + return tp_features.bluetooth ? attr->mode : 0; +} + static const struct attribute_group bluetooth_attr_group = { + .is_visible = bluetooth_attr_is_visible, .attrs = bluetooth_attributes, }; @@ -4418,11 +4357,7 @@ static void bluetooth_shutdown(void) static void bluetooth_exit(void) { - sysfs_remove_group(&tpacpi_pdev->dev.kobj, - &bluetooth_attr_group); - tpacpi_destroy_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID); - bluetooth_shutdown(); } @@ -4529,24 +4464,14 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) } if (!tp_features.bluetooth) - return 1; + return -ENODEV; res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID, &bluetooth_tprfk_ops, RFKILL_TYPE_BLUETOOTH, TPACPI_RFK_BLUETOOTH_SW_NAME, true); - if (res) - return res; - - res = sysfs_create_group(&tpacpi_pdev->dev.kobj, - &bluetooth_attr_group); - if (res) { - tpacpi_destroy_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID); - return res; - } - - return 0; + return res; } /* procfs -------------------------------------------------------------- */ @@ -4653,7 +4578,14 @@ static struct attribute *wan_attributes[] = { NULL }; +static umode_t wan_attr_is_visible(struct kobject *kobj, struct attribute *attr, + int n) +{ + return tp_features.wan ? attr->mode : 0; +} + static const struct attribute_group wan_attr_group = { + .is_visible = wan_attr_is_visible, .attrs = wan_attributes, }; @@ -4675,11 +4607,7 @@ static void wan_shutdown(void) static void wan_exit(void) { - sysfs_remove_group(&tpacpi_pdev->dev.kobj, - &wan_attr_group); - tpacpi_destroy_rfkill(TPACPI_RFK_WWAN_SW_ID); - wan_shutdown(); } @@ -4716,25 +4644,14 @@ static int __init wan_init(struct ibm_init_struct *iibm) } if (!tp_features.wan) - return 1; + return -ENODEV; res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID, &wan_tprfk_ops, RFKILL_TYPE_WWAN, TPACPI_RFK_WWAN_SW_NAME, true); - if (res) - return res; - - res = sysfs_create_group(&tpacpi_pdev->dev.kobj, - &wan_attr_group); - - if (res) { - tpacpi_destroy_rfkill(TPACPI_RFK_WWAN_SW_ID); - return res; - } - - return 0; + return res; } /* procfs -------------------------------------------------------------- */ @@ -4856,7 +4773,7 @@ static int __init uwb_init(struct ibm_init_struct *iibm) } if (!tp_features.uwb) - return 1; + return -ENODEV; res = tpacpi_new_rfkill(TPACPI_RFK_UWB_SW_ID, &uwb_tprfk_ops, @@ -4949,7 +4866,7 @@ static int __init video_init(struct ibm_init_struct *iibm) str_supported(video_supported != TPACPI_VIDEO_NONE), video_supported); - return (video_supported != TPACPI_VIDEO_NONE) ? 0 : 1; + return (video_supported != TPACPI_VIDEO_NONE) ? 0 : -ENODEV; } static void video_exit(void) @@ -5357,7 +5274,7 @@ static int __init kbdlight_init(struct ibm_init_struct *iibm) if (!kbdlight_is_supported()) { tp_features.kbdlight = 0; vdbg_printk(TPACPI_DBG_INIT, "kbdlight is unsupported\n"); - return 1; + return -ENODEV; } kbdlight_brightness = kbdlight_sysfs_get(NULL); @@ -5547,7 +5464,7 @@ static int __init light_init(struct ibm_init_struct *iibm) str_supported(tp_features.light_status)); if (!tp_features.light) - return 1; + return -ENODEV; rc = led_classdev_register(&tpacpi_pdev->dev, &tpacpi_led_thinklight.led_classdev); @@ -5635,30 +5552,35 @@ static ssize_t cmos_command_store(struct device *dev, static DEVICE_ATTR_WO(cmos_command); +static struct attribute *cmos_attributes[] = { + &dev_attr_cmos_command.attr, + NULL +}; + +static umode_t cmos_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + return cmos_handle ? attr->mode : 0; +} + +static const struct attribute_group cmos_attr_group = { + .is_visible = cmos_attr_is_visible, + .attrs = cmos_attributes, +}; + /* --------------------------------------------------------------------- */ static int __init cmos_init(struct ibm_init_struct *iibm) { - int res; - vdbg_printk(TPACPI_DBG_INIT, - "initializing cmos commands subdriver\n"); + "initializing cmos commands subdriver\n"); TPACPI_ACPIHANDLE_INIT(cmos); vdbg_printk(TPACPI_DBG_INIT, "cmos commands are %s\n", - str_supported(cmos_handle != NULL)); + str_supported(cmos_handle != NULL)); - res = device_create_file(&tpacpi_pdev->dev, &dev_attr_cmos_command); - if (res) - return res; - - return (cmos_handle) ? 0 : 1; -} - -static void cmos_exit(void) -{ - device_remove_file(&tpacpi_pdev->dev, &dev_attr_cmos_command); + return cmos_handle ? 0 : -ENODEV; } static int cmos_read(struct seq_file *m) @@ -5699,7 +5621,6 @@ static struct ibm_struct cmos_driver_data = { .name = "cmos", .read = cmos_read, .write = cmos_write, - .exit = cmos_exit, }; /************************************************************************* @@ -5738,11 +5659,11 @@ static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = { "tpacpi::standby", "tpacpi::dock_status1", "tpacpi::dock_status2", - "tpacpi::unknown_led2", + "tpacpi::lid_logo_dot", "tpacpi::unknown_led3", "tpacpi::thinkvantage", }; -#define TPACPI_SAFE_LEDS 0x1081U +#define TPACPI_SAFE_LEDS 0x1481U static inline bool tpacpi_is_led_restricted(const unsigned int led) { @@ -5904,6 +5825,7 @@ static int __init tpacpi_init_led(unsigned int led) tpacpi_leds[led].led_classdev.brightness_get = &led_sysfs_get; tpacpi_leds[led].led_classdev.name = tpacpi_led_names[led]; + tpacpi_leds[led].led_classdev.flags = LED_RETAIN_AT_SHUTDOWN; tpacpi_leds[led].led = led; return led_classdev_register(&tpacpi_pdev->dev, &tpacpi_leds[led].led_classdev); @@ -6004,7 +5926,7 @@ static int __init led_init(struct ibm_init_struct *iibm) str_supported(led_supported), led_supported); if (led_supported == TPACPI_LED_NONE) - return 1; + return -ENODEV; tpacpi_leds = kcalloc(TPACPI_LED_NUMLEDS, sizeof(*tpacpi_leds), GFP_KERNEL); @@ -6133,7 +6055,7 @@ static int __init beep_init(struct ibm_init_struct *iibm) tp_features.beep_needs_two_args = !!(quirks & TPACPI_BEEP_Q1); - return (beep_handle) ? 0 : 1; + return (beep_handle) ? 0 : -ENODEV; } static int beep_read(struct seq_file *m) @@ -6210,7 +6132,6 @@ struct ibm_thermal_sensors_struct { }; static enum thermal_access_mode thermal_read_mode; -static const struct attribute_group *thermal_attr_group; static bool thermal_use_labels; /* idx is zero-based */ @@ -6364,14 +6285,6 @@ static struct sensor_device_attribute sensor_dev_attr_thermal_temp_input[] = { &sensor_dev_attr_thermal_temp_input[X].dev_attr.attr static struct attribute *thermal_temp_input_attr[] = { - THERMAL_ATTRS(8), - THERMAL_ATTRS(9), - THERMAL_ATTRS(10), - THERMAL_ATTRS(11), - THERMAL_ATTRS(12), - THERMAL_ATTRS(13), - THERMAL_ATTRS(14), - THERMAL_ATTRS(15), THERMAL_ATTRS(0), THERMAL_ATTRS(1), THERMAL_ATTRS(2), @@ -6380,15 +6293,37 @@ static struct attribute *thermal_temp_input_attr[] = { THERMAL_ATTRS(5), THERMAL_ATTRS(6), THERMAL_ATTRS(7), + THERMAL_ATTRS(8), + THERMAL_ATTRS(9), + THERMAL_ATTRS(10), + THERMAL_ATTRS(11), + THERMAL_ATTRS(12), + THERMAL_ATTRS(13), + THERMAL_ATTRS(14), + THERMAL_ATTRS(15), NULL }; -static const struct attribute_group thermal_temp_input16_group = { - .attrs = thermal_temp_input_attr -}; +static umode_t thermal_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + if (thermal_read_mode == TPACPI_THERMAL_NONE) + return 0; -static const struct attribute_group thermal_temp_input8_group = { - .attrs = &thermal_temp_input_attr[8] + if (attr == THERMAL_ATTRS(8) || attr == THERMAL_ATTRS(9) || + attr == THERMAL_ATTRS(10) || attr == THERMAL_ATTRS(11) || + attr == THERMAL_ATTRS(12) || attr == THERMAL_ATTRS(13) || + attr == THERMAL_ATTRS(14) || attr == THERMAL_ATTRS(15)) { + if (thermal_read_mode != TPACPI_THERMAL_TPEC_16) + return 0; + } + + return attr->mode; +} + +static const struct attribute_group thermal_attr_group = { + .is_visible = thermal_attr_is_visible, + .attrs = thermal_temp_input_attr, }; #undef THERMAL_SENSOR_ATTR_TEMP @@ -6412,7 +6347,14 @@ static struct attribute *temp_label_attributes[] = { NULL }; +static umode_t temp_label_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + return thermal_use_labels ? attr->mode : 0; +} + static const struct attribute_group temp_label_attr_group = { + .is_visible = temp_label_attr_is_visible, .attrs = temp_label_attributes, }; @@ -6423,7 +6365,6 @@ static int __init thermal_init(struct ibm_init_struct *iibm) u8 t, ta1, ta2, ver = 0; int i; int acpi_tmp7; - int res; vdbg_printk(TPACPI_DBG_INIT, "initializing thermal subdriver\n"); @@ -6498,42 +6439,7 @@ static int __init thermal_init(struct ibm_init_struct *iibm) str_supported(thermal_read_mode != TPACPI_THERMAL_NONE), thermal_read_mode); - switch (thermal_read_mode) { - case TPACPI_THERMAL_TPEC_16: - thermal_attr_group = &thermal_temp_input16_group; - break; - case TPACPI_THERMAL_TPEC_8: - case TPACPI_THERMAL_ACPI_TMP07: - case TPACPI_THERMAL_ACPI_UPDT: - thermal_attr_group = &thermal_temp_input8_group; - break; - case TPACPI_THERMAL_NONE: - default: - return 1; - } - - res = sysfs_create_group(&tpacpi_hwmon->kobj, thermal_attr_group); - if (res) - return res; - - if (thermal_use_labels) { - res = sysfs_create_group(&tpacpi_hwmon->kobj, &temp_label_attr_group); - if (res) { - sysfs_remove_group(&tpacpi_hwmon->kobj, thermal_attr_group); - return res; - } - } - - return 0; -} - -static void thermal_exit(void) -{ - if (thermal_attr_group) - sysfs_remove_group(&tpacpi_hwmon->kobj, thermal_attr_group); - - if (thermal_use_labels) - sysfs_remove_group(&tpacpi_hwmon->kobj, &temp_label_attr_group); + return thermal_read_mode != TPACPI_THERMAL_NONE ? 0 : -ENODEV; } static int thermal_read(struct seq_file *m) @@ -6560,7 +6466,6 @@ static int thermal_read(struct seq_file *m) static struct ibm_struct thermal_driver_data = { .name = "thermal", .read = thermal_read, - .exit = thermal_exit, }; /************************************************************************* @@ -6945,25 +6850,25 @@ static int __init brightness_init(struct ibm_init_struct *iibm) /* if it is unknown, we don't handle it: it wouldn't be safe */ if (tp_features.bright_unkfw) - return 1; + return -ENODEV; if (!brightness_enable) { dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT, "brightness support disabled by module parameter\n"); - return 1; + return -ENODEV; } if (acpi_video_get_backlight_type() != acpi_backlight_vendor) { if (brightness_enable > 1) { pr_info("Standard ACPI backlight interface available, not loading native one\n"); - return 1; + return -ENODEV; } else if (brightness_enable == 1) { pr_warn("Cannot enable backlight brightness support, ACPI is already handling it. Refer to the acpi_backlight kernel parameter.\n"); - return 1; + return -ENODEV; } } else if (!tp_features.bright_acpimode) { pr_notice("ACPI backlight interface not available\n"); - return 1; + return -ENODEV; } pr_notice("ACPI native brightness control enabled\n"); @@ -6996,7 +6901,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm) return -EINVAL; if (tpacpi_brightness_get_raw(&b) < 0) - return 1; + return -ENODEV; memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_PLATFORM; @@ -7586,7 +7491,7 @@ static int __init volume_create_alsa_mixer(void) sizeof(struct tpacpi_alsa_data), &card); if (rc < 0 || !card) { pr_err("Failed to create ALSA card structures: %d\n", rc); - return 1; + return -ENODEV; } BUG_ON(!card->private_data); @@ -7645,7 +7550,7 @@ static int __init volume_create_alsa_mixer(void) err_exit: snd_card_free(card); - return 1; + return -ENODEV; } #define TPACPI_VOL_Q_MUTEONLY 0x0001 /* Mute-only control available */ @@ -7694,7 +7599,7 @@ static int __init volume_init(struct ibm_init_struct *iibm) if (volume_mode == TPACPI_VOL_MODE_UCMS_STEP) { pr_err("UCMS step volume mode not implemented, please contact %s\n", TPACPI_MAIL); - return 1; + return -ENODEV; } if (volume_capabilities >= TPACPI_VOL_CAP_MAX) @@ -7707,7 +7612,7 @@ static int __init volume_init(struct ibm_init_struct *iibm) if (!alsa_enable) { vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, "ALSA mixer disabled by parameter, not loading volume subdriver...\n"); - return 1; + return -ENODEV; } quirks = tpacpi_check_quirks(volume_quirk_table, @@ -7720,7 +7625,7 @@ static int __init volume_init(struct ibm_init_struct *iibm) else if (quirks & TPACPI_VOL_Q_LEVEL) tp_features.mixer_no_level_control = 0; else - return 1; /* no mixer */ + return -ENODEV; /* no mixer */ break; case TPACPI_VOL_CAP_VOLMUTE: tp_features.mixer_no_level_control = 0; @@ -7729,7 +7634,7 @@ static int __init volume_init(struct ibm_init_struct *iibm) tp_features.mixer_no_level_control = 1; break; default: - return 1; + return -ENODEV; } if (volume_capabilities != TPACPI_VOL_CAP_AUTO) @@ -7901,7 +7806,7 @@ static int __init volume_init(struct ibm_init_struct *iibm) { pr_info("volume: disabled as there is no ALSA support in this kernel\n"); - return 1; + return -ENODEV; } static struct ibm_struct volume_driver_data = { @@ -8735,17 +8640,45 @@ static ssize_t fan_watchdog_store(struct device_driver *drv, const char *buf, static DRIVER_ATTR_RW(fan_watchdog); /* --------------------------------------------------------------------- */ + static struct attribute *fan_attributes[] = { - &dev_attr_pwm1_enable.attr, &dev_attr_pwm1.attr, + &dev_attr_pwm1_enable.attr, + &dev_attr_pwm1.attr, &dev_attr_fan1_input.attr, - NULL, /* for fan2_input */ + &dev_attr_fan2_input.attr, NULL }; +static umode_t fan_attr_is_visible(struct kobject *kobj, struct attribute *attr, + int n) +{ + if (fan_status_access_mode == TPACPI_FAN_NONE && + fan_control_access_mode == TPACPI_FAN_WR_NONE) + return 0; + + if (attr == &dev_attr_fan2_input.attr) { + if (!tp_features.second_fan) + return 0; + } + + return attr->mode; +} + static const struct attribute_group fan_attr_group = { + .is_visible = fan_attr_is_visible, .attrs = fan_attributes, }; +static struct attribute *fan_driver_attributes[] = { + &driver_attr_fan_watchdog.attr, + NULL +}; + +static const struct attribute_group fan_driver_attr_group = { + .is_visible = fan_attr_is_visible, + .attrs = fan_driver_attributes, +}; + #define TPACPI_FAN_Q1 0x0001 /* Unitialized HFSP */ #define TPACPI_FAN_2FAN 0x0002 /* EC 0x31 bit 0 selects fan2 */ #define TPACPI_FAN_2CTL 0x0004 /* selects fan2 control */ @@ -8766,13 +8699,13 @@ static const struct tpacpi_quirk fan_quirk_table[] __initconst = { TPACPI_Q_LNV3('N', '2', 'E', TPACPI_FAN_2CTL), /* P1 / X1 Extreme (1st gen) */ TPACPI_Q_LNV3('N', '2', 'O', TPACPI_FAN_2CTL), /* P1 / X1 Extreme (2nd gen) */ TPACPI_Q_LNV3('N', '2', 'V', TPACPI_FAN_2CTL), /* P1 / X1 Extreme (3nd gen) */ + TPACPI_Q_LNV3('N', '4', '0', TPACPI_FAN_2CTL), /* P1 / X1 Extreme (4nd gen) */ TPACPI_Q_LNV3('N', '3', '0', TPACPI_FAN_2CTL), /* P15 (1st gen) / P15v (1st gen) */ TPACPI_Q_LNV3('N', '3', '2', TPACPI_FAN_2CTL), /* X1 Carbon (9th gen) */ }; static int __init fan_init(struct ibm_init_struct *iibm) { - int rc; unsigned long quirks; vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN, @@ -8819,7 +8752,7 @@ static int __init fan_init(struct ibm_init_struct *iibm) } } else { pr_err("ThinkPad ACPI EC access misbehaving, fan status and control unavailable\n"); - return 1; + return -ENODEV; } } @@ -8868,28 +8801,11 @@ static int __init fan_init(struct ibm_init_struct *iibm) if (fan_status_access_mode != TPACPI_FAN_NONE) fan_get_status_safe(NULL); - if (fan_status_access_mode != TPACPI_FAN_NONE || - fan_control_access_mode != TPACPI_FAN_WR_NONE) { - if (tp_features.second_fan) { - /* attach second fan tachometer */ - fan_attributes[ARRAY_SIZE(fan_attributes)-2] = - &dev_attr_fan2_input.attr; - } - rc = sysfs_create_group(&tpacpi_hwmon->kobj, - &fan_attr_group); - if (rc < 0) - return rc; + if (fan_status_access_mode == TPACPI_FAN_NONE && + fan_control_access_mode == TPACPI_FAN_WR_NONE) + return -ENODEV; - rc = driver_create_file(&tpacpi_hwmon_pdriver.driver, - &driver_attr_fan_watchdog); - if (rc < 0) { - sysfs_remove_group(&tpacpi_hwmon->kobj, - &fan_attr_group); - return rc; - } - return 0; - } else - return 1; + return 0; } static void fan_exit(void) @@ -8897,11 +8813,6 @@ static void fan_exit(void) vdbg_printk(TPACPI_DBG_EXIT | TPACPI_DBG_FAN, "cancelling any pending fan watchdog tasks\n"); - /* FIXME: can we really do this unconditionally? */ - sysfs_remove_group(&tpacpi_hwmon->kobj, &fan_attr_group); - driver_remove_file(&tpacpi_hwmon_pdriver.driver, - &driver_attr_fan_watchdog); - cancel_delayed_work(&fan_watchdog_task); flush_workqueue(tpacpi_wq); } @@ -9319,6 +9230,10 @@ static struct ibm_struct mute_led_driver_data = { #define SET_START "BCCS" #define GET_STOP "BCSG" #define SET_STOP "BCSS" +#define GET_DISCHARGE "BDSG" +#define SET_DISCHARGE "BDSS" +#define GET_INHIBIT "BICG" +#define SET_INHIBIT "BICS" enum { BAT_ANY = 0, @@ -9335,6 +9250,8 @@ enum { /* This is used in the get/set helpers */ THRESHOLD_START, THRESHOLD_STOP, + FORCE_DISCHARGE, + INHIBIT_CHARGE, }; struct tpacpi_battery_data { @@ -9342,6 +9259,7 @@ struct tpacpi_battery_data { int start_support; int charge_stop; int stop_support; + unsigned int charge_behaviours; }; struct tpacpi_battery_driver_data { @@ -9399,6 +9317,18 @@ static int tpacpi_battery_get(int what, int battery, int *ret) if (*ret == 0) *ret = 100; return 0; + case FORCE_DISCHARGE: + if ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_DISCHARGE, ret, battery)) + return -ENODEV; + /* The force discharge status is in bit 0 */ + *ret = *ret & 0x01; + return 0; + case INHIBIT_CHARGE: + if ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_INHIBIT, ret, battery)) + return -ENODEV; + /* The inhibit charge status is in bit 0 */ + *ret = *ret & 0x01; + return 0; default: pr_crit("wrong parameter: %d", what); return -EINVAL; @@ -9427,12 +9357,65 @@ static int tpacpi_battery_set(int what, int battery, int value) return -ENODEV; } return 0; + case FORCE_DISCHARGE: + /* Force discharge is in bit 0, + * break on AC attach is in bit 1 (won't work on some ThinkPads), + * battery ID is in bits 8-9, 2 bits. + */ + if (ACPI_FAILURE(tpacpi_battery_acpi_eval(SET_DISCHARGE, &ret, param))) { + pr_err("failed to set force discharge on %d", battery); + return -ENODEV; + } + return 0; + case INHIBIT_CHARGE: + /* When setting inhibit charge, we set a default value of + * always breaking on AC detach and the effective time is set to + * be permanent. + * The battery ID is in bits 4-5, 2 bits, + * the effective time is in bits 8-23, 2 bytes. + * A time of FFFF indicates forever. + */ + param = value; + param |= battery << 4; + param |= 0xFFFF << 8; + if (ACPI_FAILURE(tpacpi_battery_acpi_eval(SET_INHIBIT, &ret, param))) { + pr_err("failed to set inhibit charge on %d", battery); + return -ENODEV; + } + return 0; default: pr_crit("wrong parameter: %d", what); return -EINVAL; } } +static int tpacpi_battery_set_validate(int what, int battery, int value) +{ + int ret, v; + + ret = tpacpi_battery_set(what, battery, value); + if (ret < 0) + return ret; + + ret = tpacpi_battery_get(what, battery, &v); + if (ret < 0) + return ret; + + if (v == value) + return 0; + + msleep(500); + + ret = tpacpi_battery_get(what, battery, &v); + if (ret < 0) + return ret; + + if (v == value) + return 0; + + return -EIO; +} + static int tpacpi_battery_probe(int battery) { int ret = 0; @@ -9445,6 +9428,10 @@ static int tpacpi_battery_probe(int battery) * 2) Check for support * 3) Get the current stop threshold * 4) Check for support + * 5) Get the current force discharge status + * 6) Check for support + * 7) Get the current inhibit charge status + * 8) Check for support */ if (acpi_has_method(hkey_handle, GET_START)) { if ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_START, &ret, battery)) { @@ -9481,10 +9468,35 @@ static int tpacpi_battery_probe(int battery) return -ENODEV; } } - pr_info("battery %d registered (start %d, stop %d)", - battery, - battery_info.batteries[battery].charge_start, - battery_info.batteries[battery].charge_stop); + if (acpi_has_method(hkey_handle, GET_DISCHARGE)) { + if (ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_DISCHARGE, &ret, battery))) { + pr_err("Error probing battery discharge; %d\n", battery); + return -ENODEV; + } + /* Support is marked in bit 8 */ + if (ret & BIT(8)) + battery_info.batteries[battery].charge_behaviours |= + BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE); + } + if (acpi_has_method(hkey_handle, GET_INHIBIT)) { + if (ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_INHIBIT, &ret, battery))) { + pr_err("Error probing battery inhibit charge; %d\n", battery); + return -ENODEV; + } + /* Support is marked in bit 5 */ + if (ret & BIT(5)) + battery_info.batteries[battery].charge_behaviours |= + BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE); + } + + battery_info.batteries[battery].charge_behaviours |= + BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO); + + pr_info("battery %d registered (start %d, stop %d, behaviours: 0x%x)\n", + battery, + battery_info.batteries[battery].charge_start, + battery_info.batteries[battery].charge_stop, + battery_info.batteries[battery].charge_behaviours); return 0; } @@ -9619,6 +9631,40 @@ static ssize_t charge_control_end_threshold_show(struct device *device, return tpacpi_battery_show(THRESHOLD_STOP, device, buf); } +static ssize_t charge_behaviour_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + enum power_supply_charge_behaviour active = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO; + struct power_supply *supply = to_power_supply(dev); + unsigned int available; + int ret, battery; + + battery = tpacpi_battery_get_id(supply->desc->name); + available = battery_info.batteries[battery].charge_behaviours; + + if (available & BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE)) { + if (tpacpi_battery_get(FORCE_DISCHARGE, battery, &ret)) + return -ENODEV; + if (ret) { + active = POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE; + goto out; + } + } + + if (available & BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE)) { + if (tpacpi_battery_get(INHIBIT_CHARGE, battery, &ret)) + return -ENODEV; + if (ret) { + active = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE; + goto out; + } + } + +out: + return power_supply_charge_behaviour_show(dev, available, active, buf); +} + static ssize_t charge_control_start_threshold_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -9633,8 +9679,55 @@ static ssize_t charge_control_end_threshold_store(struct device *dev, return tpacpi_battery_store(THRESHOLD_STOP, dev, buf, count); } +static ssize_t charge_behaviour_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct power_supply *supply = to_power_supply(dev); + int selected, battery, ret = 0; + unsigned int available; + + battery = tpacpi_battery_get_id(supply->desc->name); + available = battery_info.batteries[battery].charge_behaviours; + selected = power_supply_charge_behaviour_parse(available, buf); + + if (selected < 0) + return selected; + + switch (selected) { + case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO: + if (available & BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE)) + ret = tpacpi_battery_set_validate(FORCE_DISCHARGE, battery, 0); + if (available & BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE)) + ret = min(ret, tpacpi_battery_set_validate(INHIBIT_CHARGE, battery, 0)); + if (ret < 0) + return ret; + break; + case POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE: + if (available & BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE)) + ret = tpacpi_battery_set_validate(INHIBIT_CHARGE, battery, 0); + ret = min(ret, tpacpi_battery_set_validate(FORCE_DISCHARGE, battery, 1)); + if (ret < 0) + return ret; + break; + case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE: + if (available & BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE)) + ret = tpacpi_battery_set_validate(FORCE_DISCHARGE, battery, 0); + ret = min(ret, tpacpi_battery_set_validate(INHIBIT_CHARGE, battery, 1)); + if (ret < 0) + return ret; + break; + default: + dev_err(dev, "Unexpected charge behaviour: %d\n", selected); + return -EINVAL; + } + + return count; +} + static DEVICE_ATTR_RW(charge_control_start_threshold); static DEVICE_ATTR_RW(charge_control_end_threshold); +static DEVICE_ATTR_RW(charge_behaviour); static struct device_attribute dev_attr_charge_start_threshold = __ATTR( charge_start_threshold, 0644, @@ -9653,6 +9746,7 @@ static struct attribute *tpacpi_battery_attrs[] = { &dev_attr_charge_control_end_threshold.attr, &dev_attr_charge_start_threshold.attr, &dev_attr_charge_stop_threshold.attr, + &dev_attr_charge_behaviour.attr, NULL, }; @@ -9724,69 +9818,85 @@ static struct ibm_struct battery_driver_data = { * LCD Shadow subdriver, for the Lenovo PrivacyGuard feature */ -static int lcdshadow_state; +static struct drm_privacy_screen *lcdshadow_dev; +static acpi_handle lcdshadow_get_handle; +static acpi_handle lcdshadow_set_handle; -static int lcdshadow_on_off(bool state) +static int lcdshadow_set_sw_state(struct drm_privacy_screen *priv, + enum drm_privacy_screen_status state) { - acpi_handle set_shadow_handle; int output; - if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "SSSS", &set_shadow_handle))) { - pr_warn("Thinkpad ACPI has no %s interface.\n", "SSSS"); + if (WARN_ON(!mutex_is_locked(&priv->lock))) return -EIO; - } - if (!acpi_evalf(set_shadow_handle, &output, NULL, "dd", (int)state)) + if (!acpi_evalf(lcdshadow_set_handle, &output, NULL, "dd", (int)state)) return -EIO; - lcdshadow_state = state; + priv->hw_state = priv->sw_state = state; return 0; } -static int lcdshadow_set(bool on) +static void lcdshadow_get_hw_state(struct drm_privacy_screen *priv) { - if (lcdshadow_state < 0) - return lcdshadow_state; - if (lcdshadow_state == on) - return 0; - return lcdshadow_on_off(on); + int output; + + if (!acpi_evalf(lcdshadow_get_handle, &output, NULL, "dd", 0)) + return; + + priv->hw_state = priv->sw_state = output & 0x1; } +static const struct drm_privacy_screen_ops lcdshadow_ops = { + .set_sw_state = lcdshadow_set_sw_state, + .get_hw_state = lcdshadow_get_hw_state, +}; + static int tpacpi_lcdshadow_init(struct ibm_init_struct *iibm) { - acpi_handle get_shadow_handle; + acpi_status status1, status2; int output; - if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "GSSS", &get_shadow_handle))) { - lcdshadow_state = -ENODEV; + status1 = acpi_get_handle(hkey_handle, "GSSS", &lcdshadow_get_handle); + status2 = acpi_get_handle(hkey_handle, "SSSS", &lcdshadow_set_handle); + if (ACPI_FAILURE(status1) || ACPI_FAILURE(status2)) return 0; - } - if (!acpi_evalf(get_shadow_handle, &output, NULL, "dd", 0)) { - lcdshadow_state = -EIO; + if (!acpi_evalf(lcdshadow_get_handle, &output, NULL, "dd", 0)) return -EIO; - } - if (!(output & 0x10000)) { - lcdshadow_state = -ENODEV; + + if (!(output & 0x10000)) return 0; - } - lcdshadow_state = output & 0x1; + + lcdshadow_dev = drm_privacy_screen_register(&tpacpi_pdev->dev, + &lcdshadow_ops); + if (IS_ERR(lcdshadow_dev)) + return PTR_ERR(lcdshadow_dev); return 0; } +static void lcdshadow_exit(void) +{ + drm_privacy_screen_unregister(lcdshadow_dev); +} + static void lcdshadow_resume(void) { - if (lcdshadow_state >= 0) - lcdshadow_on_off(lcdshadow_state); + if (!lcdshadow_dev) + return; + + mutex_lock(&lcdshadow_dev->lock); + lcdshadow_set_sw_state(lcdshadow_dev, lcdshadow_dev->sw_state); + mutex_unlock(&lcdshadow_dev->lock); } static int lcdshadow_read(struct seq_file *m) { - if (lcdshadow_state < 0) { + if (!lcdshadow_dev) { seq_puts(m, "status:\t\tnot supported\n"); } else { - seq_printf(m, "status:\t\t%d\n", lcdshadow_state); + seq_printf(m, "status:\t\t%d\n", lcdshadow_dev->hw_state); seq_puts(m, "commands:\t0, 1\n"); } @@ -9798,7 +9908,7 @@ static int lcdshadow_write(char *buf) char *cmd; int res, state = -EINVAL; - if (lcdshadow_state < 0) + if (!lcdshadow_dev) return -ENODEV; while ((cmd = strsep(&buf, ","))) { @@ -9810,11 +9920,18 @@ static int lcdshadow_write(char *buf) if (state >= 2 || state < 0) return -EINVAL; - return lcdshadow_set(state); + mutex_lock(&lcdshadow_dev->lock); + res = lcdshadow_set_sw_state(lcdshadow_dev, state); + mutex_unlock(&lcdshadow_dev->lock); + + drm_privacy_screen_call_notifier_chain(lcdshadow_dev); + + return res; } static struct ibm_struct lcdshadow_driver_data = { .name = "lcdshadow", + .exit = lcdshadow_exit, .resume = lcdshadow_resume, .read = lcdshadow_read, .write = lcdshadow_write, @@ -9854,33 +9971,6 @@ static int dytc_command(int command, int *output) return 0; } -static int dytc_get_version(void) -{ - int err, output; - - /* Check if we've been called before - and just return cached value */ - if (dytc_version) - return dytc_version; - - /* Otherwise query DYTC and extract version information */ - err = dytc_command(DYTC_CMD_QUERY, &output); - /* - * If support isn't available (ENODEV) then don't return an error - * and don't create the sysfs group - */ - if (err == -ENODEV) - return 0; - /* For all other errors we can flag the failure */ - if (err) - return err; - - /* Check DYTC is enabled and supports mode setting */ - if (output & BIT(DYTC_QUERY_ENABLE_BIT)) - dytc_version = (output >> DYTC_QUERY_REV_BIT) & 0xF; - - return 0; -} - static int lapsensor_get(bool *present, bool *state) { int output, err; @@ -9963,59 +10053,55 @@ static ssize_t palmsensor_show(struct device *dev, } static DEVICE_ATTR_RO(palmsensor); +static struct attribute *proxsensor_attributes[] = { + &dev_attr_dytc_lapmode.attr, + &dev_attr_palmsensor.attr, + NULL +}; + +static umode_t proxsensor_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + if (attr == &dev_attr_dytc_lapmode.attr) { + /* + * Platforms before DYTC version 5 claim to have a lap sensor, + * but it doesn't work, so we ignore them. + */ + if (!has_lapsensor || dytc_version < 5) + return 0; + } else if (attr == &dev_attr_palmsensor.attr) { + if (!has_palmsensor) + return 0; + } + + return attr->mode; +} + +static const struct attribute_group proxsensor_attr_group = { + .is_visible = proxsensor_attr_is_visible, + .attrs = proxsensor_attributes, +}; + static int tpacpi_proxsensor_init(struct ibm_init_struct *iibm) { - int palm_err, lap_err, err; + int palm_err, lap_err; palm_err = palmsensor_get(&has_palmsensor, &palm_state); lap_err = lapsensor_get(&has_lapsensor, &lap_state); - /* - * If support isn't available (ENODEV) for both devices then quit, but - * don't return an error. - */ + /* If support isn't available for both devices return -ENODEV */ if ((palm_err == -ENODEV) && (lap_err == -ENODEV)) - return 0; + return -ENODEV; /* Otherwise, if there was an error return it */ if (palm_err && (palm_err != -ENODEV)) return palm_err; if (lap_err && (lap_err != -ENODEV)) return lap_err; - if (has_palmsensor) { - err = sysfs_create_file(&tpacpi_pdev->dev.kobj, &dev_attr_palmsensor.attr); - if (err) - return err; - } - - /* Check if we know the DYTC version, if we don't then get it */ - if (!dytc_version) { - err = dytc_get_version(); - if (err) - return err; - } - /* - * Platforms before DYTC version 5 claim to have a lap sensor, but it doesn't work, so we - * ignore them - */ - if (has_lapsensor && (dytc_version >= 5)) { - err = sysfs_create_file(&tpacpi_pdev->dev.kobj, &dev_attr_dytc_lapmode.attr); - if (err) - return err; - } return 0; } -static void proxsensor_exit(void) -{ - if (has_lapsensor) - sysfs_remove_file(&tpacpi_pdev->dev.kobj, &dev_attr_dytc_lapmode.attr); - if (has_palmsensor) - sysfs_remove_file(&tpacpi_pdev->dev.kobj, &dev_attr_palmsensor.attr); -} - static struct ibm_struct proxsensor_driver_data = { .name = "proximity-sensor", - .exit = proxsensor_exit, }; /************************************************************************* @@ -10054,7 +10140,6 @@ static struct ibm_struct proxsensor_driver_data = { #define DYTC_ENABLE_CQL DYTC_SET_COMMAND(DYTC_FUNCTION_CQL, DYTC_MODE_BALANCE, 1) -static bool dytc_profile_available; static enum platform_profile_option dytc_current_profile; static atomic_t dytc_ignore_event = ATOMIC_INIT(0); static DEFINE_MUTEX(dytc_mutex); @@ -10158,9 +10243,6 @@ static int dytc_profile_set(struct platform_profile_handler *pprof, int output; int err; - if (!dytc_profile_available) - return -ENODEV; - err = mutex_lock_interruptible(&dytc_mutex); if (err) return err; @@ -10231,60 +10313,47 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm) set_bit(PLATFORM_PROFILE_BALANCED, dytc_profile.choices); set_bit(PLATFORM_PROFILE_PERFORMANCE, dytc_profile.choices); - dytc_profile_available = false; err = dytc_command(DYTC_CMD_QUERY, &output); - /* - * If support isn't available (ENODEV) then don't return an error - * and don't create the sysfs group - */ - if (err == -ENODEV) - return 0; - /* For all other errors we can flag the failure */ if (err) return err; - /* Check if we know the DYTC version, if we don't then get it */ - if (!dytc_version) { - err = dytc_get_version(); - if (err) - return err; - } + if (output & BIT(DYTC_QUERY_ENABLE_BIT)) + dytc_version = (output >> DYTC_QUERY_REV_BIT) & 0xF; + /* Check DYTC is enabled and supports mode setting */ - if (dytc_version >= 5) { - dbg_printk(TPACPI_DBG_INIT, - "DYTC version %d: thermal mode available\n", dytc_version); - /* - * Check if MMC_GET functionality available - * Version > 6 and return success from MMC_GET command - */ - dytc_mmc_get_available = false; - if (dytc_version >= 6) { - err = dytc_command(DYTC_CMD_MMC_GET, &output); - if (!err && ((output & DYTC_ERR_MASK) == DYTC_ERR_SUCCESS)) - dytc_mmc_get_available = true; - } - /* Create platform_profile structure and register */ - err = platform_profile_register(&dytc_profile); - /* - * If for some reason platform_profiles aren't enabled - * don't quit terminally. - */ - if (err) - return 0; + if (dytc_version < 5) + return -ENODEV; - dytc_profile_available = true; - /* Ensure initial values are correct */ - dytc_profile_refresh(); + dbg_printk(TPACPI_DBG_INIT, + "DYTC version %d: thermal mode available\n", dytc_version); + /* + * Check if MMC_GET functionality available + * Version > 6 and return success from MMC_GET command + */ + dytc_mmc_get_available = false; + if (dytc_version >= 6) { + err = dytc_command(DYTC_CMD_MMC_GET, &output); + if (!err && ((output & DYTC_ERR_MASK) == DYTC_ERR_SUCCESS)) + dytc_mmc_get_available = true; } + /* Create platform_profile structure and register */ + err = platform_profile_register(&dytc_profile); + /* + * If for some reason platform_profiles aren't enabled + * don't quit terminally. + */ + if (err) + return -ENODEV; + + /* Ensure initial values are correct */ + dytc_profile_refresh(); + return 0; } static void dytc_profile_exit(void) { - if (dytc_profile_available) { - dytc_profile_available = false; - platform_profile_remove(); - } + platform_profile_remove(); } static struct ibm_struct dytc_profile_driver_data = { @@ -10432,7 +10501,14 @@ static struct attribute *kbdlang_attributes[] = { NULL }; +static umode_t kbdlang_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + return tp_features.kbd_lang ? attr->mode : 0; +} + static const struct attribute_group kbdlang_attr_group = { + .is_visible = kbdlang_attr_is_visible, .attrs = kbdlang_attributes, }; @@ -10441,28 +10517,12 @@ static int tpacpi_kbdlang_init(struct ibm_init_struct *iibm) int err, output; err = get_keyboard_lang(&output); - /* - * If support isn't available (ENODEV) then don't return an error - * just don't create the sysfs group. - */ - if (err == -ENODEV) - return 0; - - if (err) - return err; - - /* Platform supports this feature - create the sysfs file */ - return sysfs_create_group(&tpacpi_pdev->dev.kobj, &kbdlang_attr_group); -} - -static void kbdlang_exit(void) -{ - sysfs_remove_group(&tpacpi_pdev->dev.kobj, &kbdlang_attr_group); + tp_features.kbd_lang = !err; + return err; } static struct ibm_struct kbdlang_driver_data = { .name = "kbdlang", - .exit = kbdlang_exit, }; /************************************************************************* @@ -10533,41 +10593,134 @@ static ssize_t wwan_antenna_type_show(struct device *dev, } static DEVICE_ATTR_RO(wwan_antenna_type); -static int tpacpi_dprc_init(struct ibm_init_struct *iibm) +static struct attribute *dprc_attributes[] = { + &dev_attr_wwan_antenna_type.attr, + NULL +}; + +static umode_t dprc_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) { - int wwanantenna_err, err; + return has_antennatype ? attr->mode : 0; +} - wwanantenna_err = get_wwan_antenna(&wwan_antennatype); - /* - * If support isn't available (ENODEV) then quit, but don't - * return an error. - */ - if (wwanantenna_err == -ENODEV) - return 0; +static const struct attribute_group dprc_attr_group = { + .is_visible = dprc_attr_is_visible, + .attrs = dprc_attributes, +}; - /* if there was an error return it */ - if (wwanantenna_err && (wwanantenna_err != -ENODEV)) - return wwanantenna_err; - else if (!wwanantenna_err) - has_antennatype = true; +static int tpacpi_dprc_init(struct ibm_init_struct *iibm) +{ + int err; - if (has_antennatype) { - err = sysfs_create_file(&tpacpi_pdev->dev.kobj, &dev_attr_wwan_antenna_type.attr); - if (err) - return err; - } + err = get_wwan_antenna(&wwan_antennatype); + if (err) + return err; + + has_antennatype = true; return 0; } -static void dprc_exit(void) +static struct ibm_struct dprc_driver_data = { + .name = "dprc", +}; + +/* --------------------------------------------------------------------- */ + +static struct attribute *tpacpi_driver_attributes[] = { + &driver_attr_debug_level.attr, + &driver_attr_version.attr, + &driver_attr_interface_version.attr, +#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES + &driver_attr_wlsw_emulstate.attr, + &driver_attr_bluetooth_emulstate.attr, + &driver_attr_wwan_emulstate.attr, + &driver_attr_uwb_emulstate.attr, +#endif + NULL +}; + +#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES +static umode_t tpacpi_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) { - if (has_antennatype) - sysfs_remove_file(&tpacpi_pdev->dev.kobj, &dev_attr_wwan_antenna_type.attr); + if (attr == &driver_attr_wlsw_emulstate.attr) { + if (!dbg_wlswemul) + return 0; + } else if (attr == &driver_attr_bluetooth_emulstate.attr) { + if (!dbg_bluetoothemul) + return 0; + } else if (attr == &driver_attr_wwan_emulstate.attr) { + if (!dbg_wwanemul) + return 0; + } else if (attr == &driver_attr_uwb_emulstate.attr) { + if (!dbg_uwbemul) + return 0; + } + + return attr->mode; } +#endif -static struct ibm_struct dprc_driver_data = { - .name = "dprc", - .exit = dprc_exit, +static const struct attribute_group tpacpi_driver_attr_group = { +#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES + .is_visible = tpacpi_attr_is_visible, +#endif + .attrs = tpacpi_driver_attributes, +}; + +static const struct attribute_group *tpacpi_driver_groups[] = { + &tpacpi_driver_attr_group, + NULL, +}; + +static const struct attribute_group *tpacpi_groups[] = { + &adaptive_kbd_attr_group, + &hotkey_attr_group, + &bluetooth_attr_group, + &wan_attr_group, + &cmos_attr_group, + &proxsensor_attr_group, + &kbdlang_attr_group, + &dprc_attr_group, + NULL, +}; + +static const struct attribute_group *tpacpi_hwmon_groups[] = { + &thermal_attr_group, + &temp_label_attr_group, + &fan_attr_group, + NULL, +}; + +static const struct attribute_group *tpacpi_hwmon_driver_groups[] = { + &fan_driver_attr_group, + NULL, +}; + +/**************************************************************************** + **************************************************************************** + * + * Platform drivers + * + **************************************************************************** + ****************************************************************************/ + +static struct platform_driver tpacpi_pdriver = { + .driver = { + .name = TPACPI_DRVR_NAME, + .pm = &tpacpi_pm, + .groups = tpacpi_driver_groups, + .dev_groups = tpacpi_groups, + }, + .shutdown = tpacpi_shutdown_handler, +}; + +static struct platform_driver tpacpi_hwmon_pdriver = { + .driver = { + .name = TPACPI_HWMON_DRVR_NAME, + .groups = tpacpi_hwmon_driver_groups, + }, }; /**************************************************************************** @@ -10624,6 +10777,20 @@ static void tpacpi_driver_event(const unsigned int hkey_event) if (!atomic_add_unless(&dytc_ignore_event, -1, 0)) dytc_profile_refresh(); } + + if (lcdshadow_dev && hkey_event == TP_HKEY_EV_PRIVACYGUARD_TOGGLE) { + enum drm_privacy_screen_status old_hw_state; + bool changed; + + mutex_lock(&lcdshadow_dev->lock); + old_hw_state = lcdshadow_dev->hw_state; + lcdshadow_get_hw_state(lcdshadow_dev); + changed = lcdshadow_dev->hw_state != old_hw_state; + mutex_unlock(&lcdshadow_dev->lock); + + if (changed) + drm_privacy_screen_call_notifier_chain(lcdshadow_dev); + } } static void hotkey_driver_event(const unsigned int scancode) @@ -10710,8 +10877,8 @@ static int __init ibm_init(struct ibm_init_struct *iibm) if (iibm->init) { ret = iibm->init(iibm); - if (ret > 0) - return 0; /* probe failed */ + if (ret > 0 || ret == -ENODEV) + return 0; /* subdriver functionality not available */ if (ret) return ret; @@ -11090,8 +11257,6 @@ static int __init set_ibm_param(const char *val, const struct kernel_param *kp) for (i = 0; i < ARRAY_SIZE(ibms_init); i++) { ibm = ibms_init[i].data; - WARN_ON(ibm == NULL); - if (!ibm || !ibm->name) continue; @@ -11203,6 +11368,13 @@ static void thinkpad_acpi_module_exit(void) tpacpi_lifecycle = TPACPI_LIFE_EXITING; + if (tpacpi_hwmon) + hwmon_device_unregister(tpacpi_hwmon); + if (tp_features.sensors_pdrv_registered) + platform_driver_unregister(&tpacpi_hwmon_pdriver); + if (tp_features.platform_drv_registered) + platform_driver_unregister(&tpacpi_pdriver); + list_for_each_entry_safe_reverse(ibm, itmp, &tpacpi_all_drivers, all_drivers) { @@ -11219,28 +11391,12 @@ static void thinkpad_acpi_module_exit(void) kfree(hotkey_keycode_map); } - if (tpacpi_hwmon) - hwmon_device_unregister(tpacpi_hwmon); - if (tpacpi_sensors_pdev) platform_device_unregister(tpacpi_sensors_pdev); if (tpacpi_pdev) platform_device_unregister(tpacpi_pdev); - - if (tp_features.sensors_pdrv_attrs_registered) - tpacpi_remove_driver_attributes(&tpacpi_hwmon_pdriver.driver); - if (tp_features.platform_drv_attrs_registered) - tpacpi_remove_driver_attributes(&tpacpi_pdriver.driver); - - if (tp_features.sensors_pdrv_registered) - platform_driver_unregister(&tpacpi_hwmon_pdriver); - - if (tp_features.platform_drv_registered) - platform_driver_unregister(&tpacpi_pdriver); - if (proc_dir) remove_proc_entry(TPACPI_PROC_DIR, acpi_root_dir); - if (tpacpi_wq) destroy_workqueue(tpacpi_wq); @@ -11292,36 +11448,6 @@ static int __init thinkpad_acpi_module_init(void) return -ENODEV; } - ret = platform_driver_register(&tpacpi_pdriver); - if (ret) { - pr_err("unable to register main platform driver\n"); - thinkpad_acpi_module_exit(); - return ret; - } - tp_features.platform_drv_registered = 1; - - ret = platform_driver_register(&tpacpi_hwmon_pdriver); - if (ret) { - pr_err("unable to register hwmon platform driver\n"); - thinkpad_acpi_module_exit(); - return ret; - } - tp_features.sensors_pdrv_registered = 1; - - ret = tpacpi_create_driver_attributes(&tpacpi_pdriver.driver); - if (!ret) { - tp_features.platform_drv_attrs_registered = 1; - ret = tpacpi_create_driver_attributes( - &tpacpi_hwmon_pdriver.driver); - } - if (ret) { - pr_err("unable to create sysfs driver attributes\n"); - thinkpad_acpi_module_exit(); - return ret; - } - tp_features.sensors_pdrv_attrs_registered = 1; - - /* Device initialization */ tpacpi_pdev = platform_device_register_simple(TPACPI_DRVR_NAME, -1, NULL, 0); @@ -11342,17 +11468,7 @@ static int __init thinkpad_acpi_module_init(void) thinkpad_acpi_module_exit(); return ret; } - tp_features.sensors_pdev_attrs_registered = 1; - tpacpi_hwmon = hwmon_device_register_with_groups( - &tpacpi_sensors_pdev->dev, TPACPI_NAME, NULL, NULL); - if (IS_ERR(tpacpi_hwmon)) { - ret = PTR_ERR(tpacpi_hwmon); - tpacpi_hwmon = NULL; - pr_err("unable to register hwmon device\n"); - thinkpad_acpi_module_exit(); - return ret; - } mutex_init(&tpacpi_inputdev_send_mutex); tpacpi_inputdev = input_allocate_device(); if (!tpacpi_inputdev) { @@ -11385,6 +11501,32 @@ static int __init thinkpad_acpi_module_init(void) tpacpi_lifecycle = TPACPI_LIFE_RUNNING; + ret = platform_driver_register(&tpacpi_pdriver); + if (ret) { + pr_err("unable to register main platform driver\n"); + thinkpad_acpi_module_exit(); + return ret; + } + tp_features.platform_drv_registered = 1; + + ret = platform_driver_register(&tpacpi_hwmon_pdriver); + if (ret) { + pr_err("unable to register hwmon platform driver\n"); + thinkpad_acpi_module_exit(); + return ret; + } + tp_features.sensors_pdrv_registered = 1; + + tpacpi_hwmon = hwmon_device_register_with_groups( + &tpacpi_sensors_pdev->dev, TPACPI_NAME, NULL, tpacpi_hwmon_groups); + if (IS_ERR(tpacpi_hwmon)) { + ret = PTR_ERR(tpacpi_hwmon); + tpacpi_hwmon = NULL; + pr_err("unable to register hwmon device\n"); + thinkpad_acpi_module_exit(); + return ret; + } + ret = input_register_device(tpacpi_inputdev); if (ret < 0) { pr_err("unable to register input device\n"); |