diff options
Diffstat (limited to 'drivers/platform/x86/thinkpad_acpi.c')
| -rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 1143 | 
1 files changed, 648 insertions, 495 deletions
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index b3ac9c3f3b7c..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   */ @@ -1479,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   */ @@ -2999,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,  }; @@ -3015,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, @@ -3094,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 @@ -3434,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)); @@ -3490,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; @@ -3527,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 { @@ -3551,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, @@ -3566,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); @@ -3648,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 @@ -3786,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) @@ -3820,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; @@ -4384,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,  }; @@ -4406,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();  } @@ -4517,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 -------------------------------------------------------------- */ @@ -4641,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,  }; @@ -4663,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();  } @@ -4704,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 -------------------------------------------------------------- */ @@ -4844,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, @@ -4937,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) @@ -5345,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); @@ -5535,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); @@ -5623,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)); - -	res = device_create_file(&tpacpi_pdev->dev, &dev_attr_cmos_command); -	if (res) -		return res; - -	return (cmos_handle) ? 0 : 1; -} +		    str_supported(cmos_handle != NULL)); -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) @@ -5687,7 +5621,6 @@ static struct ibm_struct cmos_driver_data = {  	.name = "cmos",  	.read = cmos_read,  	.write = cmos_write, -	.exit = cmos_exit,  };  /************************************************************************* @@ -5726,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)  { @@ -5892,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); @@ -5992,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); @@ -6121,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) @@ -6198,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 */ @@ -6352,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), @@ -6368,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; + +	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_temp_input8_group = { -	.attrs = &thermal_temp_input_attr[8] +static const struct attribute_group thermal_attr_group = { +	.is_visible = thermal_attr_is_visible, +	.attrs = thermal_temp_input_attr,  };  #undef THERMAL_SENSOR_ATTR_TEMP @@ -6400,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,  }; @@ -6411,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"); @@ -6486,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) @@ -6548,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,  };  /************************************************************************* @@ -6933,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"); @@ -6984,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; @@ -7574,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); @@ -7633,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 */ @@ -7682,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) @@ -7695,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, @@ -7708,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; @@ -7717,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) @@ -7889,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 = { @@ -8723,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 */ @@ -8761,7 +8706,6 @@ static const struct tpacpi_quirk fan_quirk_table[] __initconst = {  static int __init fan_init(struct ibm_init_struct *iibm)  { -	int rc;  	unsigned long quirks;  	vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN, @@ -8808,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;  		}  	} @@ -8857,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) @@ -8886,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);  } @@ -9308,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, @@ -9324,6 +9250,8 @@ enum {  	/* This is used in the get/set helpers */  	THRESHOLD_START,  	THRESHOLD_STOP, +	FORCE_DISCHARGE, +	INHIBIT_CHARGE,  };  struct tpacpi_battery_data { @@ -9331,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 { @@ -9388,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; @@ -9416,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; @@ -9434,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)) { @@ -9470,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;  } @@ -9608,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) @@ -9622,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, @@ -9642,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,  }; @@ -9713,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");  	} @@ -9787,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, ","))) { @@ -9799,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, @@ -9843,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; @@ -9952,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,  };  /************************************************************************* @@ -10043,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); @@ -10147,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; @@ -10220,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 = { @@ -10421,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,  }; @@ -10430,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,  };  /************************************************************************* @@ -10522,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, +}; + +static int tpacpi_dprc_init(struct ibm_init_struct *iibm) +{ +	int err; -	/* if there was an error return it */ -	if (wwanantenna_err && (wwanantenna_err != -ENODEV)) -		return wwanantenna_err; -	else if (!wwanantenna_err) -		has_antennatype = true; +	err = get_wwan_antenna(&wwan_antennatype); +	if (err) +		return err; -	if (has_antennatype) { -		err = sysfs_create_file(&tpacpi_pdev->dev.kobj, &dev_attr_wwan_antenna_type.attr); -		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, +	},  };  /**************************************************************************** @@ -10613,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) @@ -10699,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; @@ -11079,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; @@ -11192,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) { @@ -11208,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); @@ -11281,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); @@ -11331,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) { @@ -11374,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");  |