File Index
Files and directories
Sign in
FILE

samsung-galaxybook.c

File preview with syntax highlighting.

linux-6.19.14/drivers/platform/x86/samsung-galaxybook.c C
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Samsung Galaxy Book driver
   4 *
   5 * Copyright (c) 2025 Joshua Grisham <josh@joshuagrisham.com>
   6 *
   7 * With contributions to the SCAI ACPI device interface:
   8 * Copyright (c) 2024 Giulio Girardi <giulio.girardi@protechgroup.it>
   9 *
  10 * Implementation inspired by existing x86 platform drivers.
  11 * Thank you to the authors!
  12 */
  13
  14#include <linux/acpi.h>
  15#include <linux/bits.h>
  16#include <linux/err.h>
  17#include <linux/i8042.h>
  18#include <linux/init.h>
  19#include <linux/input.h>
  20#include <linux/kernel.h>
  21#include <linux/leds.h>
  22#include <linux/module.h>
  23#include <linux/mutex.h>
  24#include <linux/platform_device.h>
  25#include <linux/platform_profile.h>
  26#include <linux/serio.h>
  27#include <linux/sysfs.h>
  28#include <linux/uuid.h>
  29#include <linux/workqueue.h>
  30#include <acpi/battery.h>
  31#include "firmware_attributes_class.h"
  32
  33#define DRIVER_NAME "samsung-galaxybook"
  34
  35struct samsung_galaxybook {
  36	struct platform_device *platform;
  37	struct acpi_device *acpi;
  38
  39	struct device *fw_attrs_dev;
  40	struct kset *fw_attrs_kset;
  41	/* block in case firmware attributes are updated in multiple threads */
  42	struct mutex fw_attr_lock;
  43
  44	bool has_kbd_backlight;
  45	bool has_block_recording;
  46	bool has_performance_mode;
  47
  48	struct led_classdev kbd_backlight;
  49	struct work_struct kbd_backlight_hotkey_work;
  50	/* block in case brightness updated using hotkey and another thread */
  51	struct mutex kbd_backlight_lock;
  52
  53	void *i8042_filter_ptr;
  54
  55	struct work_struct block_recording_hotkey_work;
  56	struct input_dev *camera_lens_cover_switch;
  57
  58	struct acpi_battery_hook battery_hook;
  59
  60	u8 profile_performance_modes[PLATFORM_PROFILE_LAST];
  61};
  62
  63enum galaxybook_fw_attr_id {
  64	GB_ATTR_POWER_ON_LID_OPEN,
  65	GB_ATTR_USB_CHARGING,
  66	GB_ATTR_BLOCK_RECORDING,
  67};
  68
  69static const char * const galaxybook_fw_attr_name[] = {
  70	[GB_ATTR_POWER_ON_LID_OPEN] = "power_on_lid_open",
  71	[GB_ATTR_USB_CHARGING]      = "usb_charging",
  72	[GB_ATTR_BLOCK_RECORDING]   = "block_recording",
  73};
  74
  75static const char * const galaxybook_fw_attr_desc[] = {
  76	[GB_ATTR_POWER_ON_LID_OPEN] = "Power On Lid Open",
  77	[GB_ATTR_USB_CHARGING]      = "USB Charging",
  78	[GB_ATTR_BLOCK_RECORDING]   = "Block Recording",
  79};
  80
  81#define GB_ATTR_LANGUAGE_CODE "en_US.UTF-8"
  82
  83struct galaxybook_fw_attr {
  84	struct samsung_galaxybook *galaxybook;
  85	enum galaxybook_fw_attr_id fw_attr_id;
  86	struct attribute_group attr_group;
  87	struct kobj_attribute display_name;
  88	struct kobj_attribute current_value;
  89	int (*get_value)(struct samsung_galaxybook *galaxybook, bool *value);
  90	int (*set_value)(struct samsung_galaxybook *galaxybook, const bool value);
  91};
  92
  93struct sawb {
  94	u16 safn;
  95	u16 sasb;
  96	u8 rflg;
  97	union {
  98		struct {
  99			u8 gunm;
 100			u8 guds[250];
 101		} __packed;
 102		struct {
 103			u8 caid[16];
 104			u8 fncn;
 105			u8 subn;
 106			u8 iob0;
 107			u8 iob1;
 108			u8 iob2;
 109			u8 iob3;
 110			u8 iob4;
 111			u8 iob5;
 112			u8 iob6;
 113			u8 iob7;
 114			u8 iob8;
 115			u8 iob9;
 116		} __packed;
 117		struct {
 118			u8 iob_prefix[18];
 119			u8 iobs[10];
 120		} __packed;
 121	} __packed;
 122} __packed;
 123
 124#define GB_SAWB_LEN_SETTINGS          0x15
 125#define GB_SAWB_LEN_PERFORMANCE_MODE  0x100
 126
 127#define GB_SAFN  0x5843
 128
 129#define GB_SASB_KBD_BACKLIGHT     0x78
 130#define GB_SASB_POWER_MANAGEMENT  0x7a
 131#define GB_SASB_USB_CHARGING_GET  0x67
 132#define GB_SASB_USB_CHARGING_SET  0x68
 133#define GB_SASB_NOTIFICATIONS     0x86
 134#define GB_SASB_BLOCK_RECORDING   0x8a
 135#define GB_SASB_PERFORMANCE_MODE  0x91
 136
 137#define GB_SAWB_RFLG_POS     4
 138#define GB_SAWB_GB_GUNM_POS  5
 139
 140#define GB_RFLG_SUCCESS  0xaa
 141#define GB_GUNM_FAIL     0xff
 142
 143#define GB_GUNM_FEATURE_ENABLE          0xbb
 144#define GB_GUNM_FEATURE_ENABLE_SUCCESS  0xdd
 145#define GB_GUDS_FEATURE_ENABLE          0xaa
 146#define GB_GUDS_FEATURE_ENABLE_SUCCESS  0xcc
 147
 148#define GB_GUNM_GET  0x81
 149#define GB_GUNM_SET  0x82
 150
 151#define GB_GUNM_POWER_MANAGEMENT  0x82
 152
 153#define GB_GUNM_USB_CHARGING_GET            0x80
 154#define GB_GUNM_USB_CHARGING_ON             0x81
 155#define GB_GUNM_USB_CHARGING_OFF            0x80
 156#define GB_GUDS_POWER_ON_LID_OPEN           0xa3
 157#define GB_GUDS_POWER_ON_LID_OPEN_GET       0x81
 158#define GB_GUDS_POWER_ON_LID_OPEN_SET       0x80
 159#define GB_GUDS_BATTERY_CHARGE_CONTROL      0xe9
 160#define GB_GUDS_BATTERY_CHARGE_CONTROL_GET  0x91
 161#define GB_GUDS_BATTERY_CHARGE_CONTROL_SET  0x90
 162#define GB_GUNM_ACPI_NOTIFY_ENABLE          0x80
 163#define GB_GUDS_ACPI_NOTIFY_ENABLE          0x02
 164
 165#define GB_BLOCK_RECORDING_ON   0x0
 166#define GB_BLOCK_RECORDING_OFF  0x1
 167
 168#define GB_FNCN_PERFORMANCE_MODE       0x51
 169#define GB_SUBN_PERFORMANCE_MODE_LIST  0x01
 170#define GB_SUBN_PERFORMANCE_MODE_GET   0x02
 171#define GB_SUBN_PERFORMANCE_MODE_SET   0x03
 172
 173/* guid 8246028d-8bca-4a55-ba0f-6f1e6b921b8f */
 174static const guid_t performance_mode_guid =
 175	GUID_INIT(0x8246028d, 0x8bca, 0x4a55, 0xba, 0x0f, 0x6f, 0x1e, 0x6b, 0x92, 0x1b, 0x8f);
 176#define GB_PERFORMANCE_MODE_GUID performance_mode_guid
 177
 178#define GB_PERFORMANCE_MODE_FANOFF          0xb
 179#define GB_PERFORMANCE_MODE_LOWNOISE        0xa
 180#define GB_PERFORMANCE_MODE_OPTIMIZED       0x0
 181#define GB_PERFORMANCE_MODE_OPTIMIZED_V2    0x2
 182#define GB_PERFORMANCE_MODE_PERFORMANCE     0x1
 183#define GB_PERFORMANCE_MODE_PERFORMANCE_V2  0x15
 184#define GB_PERFORMANCE_MODE_ULTRA           0x16
 185#define GB_PERFORMANCE_MODE_IGNORE1         0x14
 186#define GB_PERFORMANCE_MODE_IGNORE2         0xc
 187
 188#define GB_ACPI_METHOD_ENABLE            "SDLS"
 189#define GB_ACPI_METHOD_ENABLE_ON         1
 190#define GB_ACPI_METHOD_ENABLE_OFF        0
 191#define GB_ACPI_METHOD_SETTINGS          "CSFI"
 192#define GB_ACPI_METHOD_PERFORMANCE_MODE  "CSXI"
 193
 194#define GB_KBD_BACKLIGHT_MAX_BRIGHTNESS  3
 195
 196#define GB_ACPI_NOTIFY_BATTERY_STATE_CHANGED    0x61
 197#define GB_ACPI_NOTIFY_DEVICE_ON_TABLE          0x6c
 198#define GB_ACPI_NOTIFY_DEVICE_OFF_TABLE         0x6d
 199#define GB_ACPI_NOTIFY_HOTKEY_PERFORMANCE_MODE  0x70
 200
 201#define GB_KEY_KBD_BACKLIGHT_KEYDOWN    0x2c
 202#define GB_KEY_KBD_BACKLIGHT_KEYUP      0xac
 203#define GB_KEY_BLOCK_RECORDING_KEYDOWN  0x1f
 204#define GB_KEY_BLOCK_RECORDING_KEYUP    0x9f
 205#define GB_KEY_BATTERY_NOTIFY_KEYUP     0xf
 206#define GB_KEY_BATTERY_NOTIFY_KEYDOWN   0x8f
 207
 208/*
 209 * Optional features which have been determined as not supported on a particular
 210 * device will return GB_NOT_SUPPORTED from their init function. Positive
 211 * EOPNOTSUPP is used as the underlying value instead of negative to
 212 * differentiate this return code from valid upstream failures.
 213 */
 214#define GB_NOT_SUPPORTED EOPNOTSUPP /* Galaxy Book feature not supported */
 215
 216/*
 217 * ACPI method handling
 218 */
 219
 220static int galaxybook_acpi_method(struct samsung_galaxybook *galaxybook, acpi_string method,
 221				  struct sawb *buf, size_t len)
 222{
 223	struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
 224	union acpi_object in_obj, *out_obj;
 225	struct acpi_object_list input;
 226	acpi_status status;
 227	int err;
 228
 229	in_obj.type = ACPI_TYPE_BUFFER;
 230	in_obj.buffer.length = len;
 231	in_obj.buffer.pointer = (u8 *)buf;
 232
 233	input.count = 1;
 234	input.pointer = &in_obj;
 235
 236	status = acpi_evaluate_object_typed(galaxybook->acpi->handle, method, &input, &output,
 237					    ACPI_TYPE_BUFFER);
 238
 239	if (ACPI_FAILURE(status)) {
 240		dev_err(&galaxybook->acpi->dev, "failed to execute method %s; got %s\n",
 241			method, acpi_format_exception(status));
 242		return -EIO;
 243	}
 244
 245	out_obj = output.pointer;
 246
 247	if (out_obj->buffer.length != len || out_obj->buffer.length < GB_SAWB_GB_GUNM_POS + 1) {
 248		dev_err(&galaxybook->acpi->dev,
 249			"failed to execute %s; response length mismatch\n",
 250			method);
 251		err = -EPROTO;
 252		goto out_free;
 253	}
 254	if (out_obj->buffer.pointer[GB_SAWB_RFLG_POS] != GB_RFLG_SUCCESS) {
 255		dev_err(&galaxybook->acpi->dev,
 256			"failed to execute %s; device did not respond with success code 0x%x\n",
 257			method, GB_RFLG_SUCCESS);
 258		err = -ENXIO;
 259		goto out_free;
 260	}
 261	if (out_obj->buffer.pointer[GB_SAWB_GB_GUNM_POS] == GB_GUNM_FAIL) {
 262		dev_err(&galaxybook->acpi->dev,
 263			"failed to execute %s; device responded with failure code 0x%x\n",
 264			method, GB_GUNM_FAIL);
 265		err = -ENXIO;
 266		goto out_free;
 267	}
 268
 269	memcpy(buf, out_obj->buffer.pointer, len);
 270	err = 0;
 271
 272out_free:
 273	kfree(out_obj);
 274	return err;
 275}
 276
 277static int galaxybook_enable_acpi_feature(struct samsung_galaxybook *galaxybook, const u16 sasb)
 278{
 279	struct sawb buf = {};
 280	int err;
 281
 282	buf.safn = GB_SAFN;
 283	buf.sasb = sasb;
 284	buf.gunm = GB_GUNM_FEATURE_ENABLE;
 285	buf.guds[0] = GB_GUDS_FEATURE_ENABLE;
 286
 287	err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
 288				     &buf, GB_SAWB_LEN_SETTINGS);
 289	if (err)
 290		return err;
 291
 292	if (buf.gunm != GB_GUNM_FEATURE_ENABLE_SUCCESS &&
 293	    buf.guds[0] != GB_GUDS_FEATURE_ENABLE_SUCCESS)
 294		return -ENODEV;
 295
 296	return 0;
 297}
 298
 299/*
 300 * Keyboard Backlight
 301 */
 302
 303static int kbd_backlight_acpi_get(struct samsung_galaxybook *galaxybook,
 304				  enum led_brightness *brightness)
 305{
 306	struct sawb buf = {};
 307	int err;
 308
 309	buf.safn = GB_SAFN;
 310	buf.sasb = GB_SASB_KBD_BACKLIGHT;
 311	buf.gunm = GB_GUNM_GET;
 312
 313	err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
 314				     &buf, GB_SAWB_LEN_SETTINGS);
 315	if (err)
 316		return err;
 317
 318	*brightness = buf.gunm;
 319
 320	return 0;
 321}
 322
 323static int kbd_backlight_acpi_set(struct samsung_galaxybook *galaxybook,
 324				  const enum led_brightness brightness)
 325{
 326	struct sawb buf = {};
 327
 328	buf.safn = GB_SAFN;
 329	buf.sasb = GB_SASB_KBD_BACKLIGHT;
 330	buf.gunm = GB_GUNM_SET;
 331
 332	buf.guds[0] = brightness;
 333
 334	return galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
 335				      &buf, GB_SAWB_LEN_SETTINGS);
 336}
 337
 338static enum led_brightness kbd_backlight_show(struct led_classdev *led)
 339{
 340	struct samsung_galaxybook *galaxybook =
 341		container_of(led, struct samsung_galaxybook, kbd_backlight);
 342	enum led_brightness brightness;
 343	int err;
 344
 345	err = kbd_backlight_acpi_get(galaxybook, &brightness);
 346	if (err)
 347		return err;
 348
 349	return brightness;
 350}
 351
 352static int kbd_backlight_store(struct led_classdev *led,
 353			       const enum led_brightness brightness)
 354{
 355	struct samsung_galaxybook *galaxybook =
 356		container_of_const(led, struct samsung_galaxybook, kbd_backlight);
 357
 358	return kbd_backlight_acpi_set(galaxybook, brightness);
 359}
 360
 361static int galaxybook_kbd_backlight_init(struct samsung_galaxybook *galaxybook)
 362{
 363	struct led_init_data init_data = {};
 364	enum led_brightness brightness;
 365	int err;
 366
 367	err = devm_mutex_init(&galaxybook->platform->dev, &galaxybook->kbd_backlight_lock);
 368	if (err)
 369		return err;
 370
 371	err = galaxybook_enable_acpi_feature(galaxybook, GB_SASB_KBD_BACKLIGHT);
 372	if (err) {
 373		dev_dbg(&galaxybook->platform->dev,
 374			"failed to enable kbd_backlight feature, error %d\n", err);
 375		return GB_NOT_SUPPORTED;
 376	}
 377
 378	err = kbd_backlight_acpi_get(galaxybook, &brightness);
 379	if (err) {
 380		dev_dbg(&galaxybook->platform->dev,
 381			"failed to get initial kbd_backlight brightness, error %d\n", err);
 382		return GB_NOT_SUPPORTED;
 383	}
 384
 385	init_data.devicename = DRIVER_NAME;
 386	init_data.default_label = ":" LED_FUNCTION_KBD_BACKLIGHT;
 387	init_data.devname_mandatory = true;
 388
 389	galaxybook->kbd_backlight.brightness_get = kbd_backlight_show;
 390	galaxybook->kbd_backlight.brightness_set_blocking = kbd_backlight_store;
 391	galaxybook->kbd_backlight.flags = LED_BRIGHT_HW_CHANGED;
 392	galaxybook->kbd_backlight.max_brightness = GB_KBD_BACKLIGHT_MAX_BRIGHTNESS;
 393
 394	return devm_led_classdev_register_ext(&galaxybook->platform->dev,
 395					      &galaxybook->kbd_backlight, &init_data);
 396}
 397
 398/*
 399 * Battery Extension (adds charge_control_end_threshold to the battery device)
 400 */
 401
 402static int charge_control_end_threshold_acpi_get(struct samsung_galaxybook *galaxybook, u8 *value)
 403{
 404	struct sawb buf = {};
 405	int err;
 406
 407	buf.safn = GB_SAFN;
 408	buf.sasb = GB_SASB_POWER_MANAGEMENT;
 409	buf.gunm = GB_GUNM_POWER_MANAGEMENT;
 410	buf.guds[0] = GB_GUDS_BATTERY_CHARGE_CONTROL;
 411	buf.guds[1] = GB_GUDS_BATTERY_CHARGE_CONTROL_GET;
 412
 413	err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
 414				     &buf, GB_SAWB_LEN_SETTINGS);
 415	if (err)
 416		return err;
 417
 418	*value = buf.guds[1];
 419
 420	return 0;
 421}
 422
 423static int charge_control_end_threshold_acpi_set(struct samsung_galaxybook *galaxybook, u8 value)
 424{
 425	struct sawb buf = {};
 426
 427	buf.safn = GB_SAFN;
 428	buf.sasb = GB_SASB_POWER_MANAGEMENT;
 429	buf.gunm = GB_GUNM_POWER_MANAGEMENT;
 430	buf.guds[0] = GB_GUDS_BATTERY_CHARGE_CONTROL;
 431	buf.guds[1] = GB_GUDS_BATTERY_CHARGE_CONTROL_SET;
 432	buf.guds[2] = value;
 433
 434	return galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
 435				      &buf, GB_SAWB_LEN_SETTINGS);
 436}
 437
 438static int galaxybook_battery_ext_property_get(struct power_supply *psy,
 439					       const struct power_supply_ext *ext,
 440					       void *ext_data,
 441					       enum power_supply_property psp,
 442					       union power_supply_propval *val)
 443{
 444	struct samsung_galaxybook *galaxybook = ext_data;
 445	u8 value;
 446	int err;
 447
 448	if (psp != POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD)
 449		return -EINVAL;
 450
 451	err = charge_control_end_threshold_acpi_get(galaxybook, &value);
 452	if (err)
 453		return err;
 454
 455	/*
 456	 * device stores "no end threshold" as 0 instead of 100;
 457	 * if device has 0, report 100
 458	 */
 459	if (value == 0)
 460		value = 100;
 461
 462	val->intval = value;
 463
 464	return 0;
 465}
 466
 467static int galaxybook_battery_ext_property_set(struct power_supply *psy,
 468					       const struct power_supply_ext *ext,
 469					       void *ext_data,
 470					       enum power_supply_property psp,
 471					       const union power_supply_propval *val)
 472{
 473	struct samsung_galaxybook *galaxybook = ext_data;
 474	u8 value;
 475
 476	if (psp != POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD)
 477		return -EINVAL;
 478
 479	value = val->intval;
 480
 481	if (value < 1 || value > 100)
 482		return -EINVAL;
 483
 484	/*
 485	 * device stores "no end threshold" as 0 instead of 100;
 486	 * if setting to 100, send 0
 487	 */
 488	if (value == 100)
 489		value = 0;
 490
 491	return charge_control_end_threshold_acpi_set(galaxybook, value);
 492}
 493
 494static int galaxybook_battery_ext_property_is_writeable(struct power_supply *psy,
 495							const struct power_supply_ext *ext,
 496							void *ext_data,
 497							enum power_supply_property psp)
 498{
 499	if (psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD)
 500		return true;
 501
 502	return false;
 503}
 504
 505static const enum power_supply_property galaxybook_battery_properties[] = {
 506	POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD,
 507};
 508
 509static const struct power_supply_ext galaxybook_battery_ext = {
 510	.name			= DRIVER_NAME,
 511	.properties		= galaxybook_battery_properties,
 512	.num_properties		= ARRAY_SIZE(galaxybook_battery_properties),
 513	.get_property		= galaxybook_battery_ext_property_get,
 514	.set_property		= galaxybook_battery_ext_property_set,
 515	.property_is_writeable	= galaxybook_battery_ext_property_is_writeable,
 516};
 517
 518static int galaxybook_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook)
 519{
 520	struct samsung_galaxybook *galaxybook =
 521		container_of(hook, struct samsung_galaxybook, battery_hook);
 522
 523	return power_supply_register_extension(battery, &galaxybook_battery_ext,
 524					       &battery->dev, galaxybook);
 525}
 526
 527static int galaxybook_battery_remove(struct power_supply *battery, struct acpi_battery_hook *hook)
 528{
 529	power_supply_unregister_extension(battery, &galaxybook_battery_ext);
 530	return 0;
 531}
 532
 533static int galaxybook_battery_threshold_init(struct samsung_galaxybook *galaxybook)
 534{
 535	u8 value;
 536	int err;
 537
 538	err = charge_control_end_threshold_acpi_get(galaxybook, &value);
 539	if (err) {
 540		dev_dbg(&galaxybook->platform->dev,
 541			"failed to get initial battery charge end threshold, error %d\n", err);
 542		return 0;
 543	}
 544
 545	galaxybook->battery_hook.add_battery = galaxybook_battery_add;
 546	galaxybook->battery_hook.remove_battery = galaxybook_battery_remove;
 547	galaxybook->battery_hook.name = "Samsung Galaxy Book Battery Extension";
 548
 549	return devm_battery_hook_register(&galaxybook->platform->dev, &galaxybook->battery_hook);
 550}
 551
 552/*
 553 * Platform Profile / Performance mode
 554 */
 555
 556static int performance_mode_acpi_get(struct samsung_galaxybook *galaxybook, u8 *performance_mode)
 557{
 558	struct sawb buf = {};
 559	int err;
 560
 561	buf.safn = GB_SAFN;
 562	buf.sasb = GB_SASB_PERFORMANCE_MODE;
 563	export_guid(buf.caid, &GB_PERFORMANCE_MODE_GUID);
 564	buf.fncn = GB_FNCN_PERFORMANCE_MODE;
 565	buf.subn = GB_SUBN_PERFORMANCE_MODE_GET;
 566
 567	err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_PERFORMANCE_MODE,
 568				     &buf, GB_SAWB_LEN_PERFORMANCE_MODE);
 569	if (err)
 570		return err;
 571
 572	*performance_mode = buf.iob0;
 573
 574	return 0;
 575}
 576
 577static int performance_mode_acpi_set(struct samsung_galaxybook *galaxybook,
 578				     const u8 performance_mode)
 579{
 580	struct sawb buf = {};
 581
 582	buf.safn = GB_SAFN;
 583	buf.sasb = GB_SASB_PERFORMANCE_MODE;
 584	export_guid(buf.caid, &GB_PERFORMANCE_MODE_GUID);
 585	buf.fncn = GB_FNCN_PERFORMANCE_MODE;
 586	buf.subn = GB_SUBN_PERFORMANCE_MODE_SET;
 587	buf.iob0 = performance_mode;
 588
 589	return galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_PERFORMANCE_MODE,
 590				      &buf, GB_SAWB_LEN_PERFORMANCE_MODE);
 591}
 592
 593static int get_performance_mode_profile(struct samsung_galaxybook *galaxybook,
 594					const u8 performance_mode,
 595					enum platform_profile_option *profile)
 596{
 597	switch (performance_mode) {
 598	case GB_PERFORMANCE_MODE_FANOFF:
 599		*profile = PLATFORM_PROFILE_LOW_POWER;
 600		break;
 601	case GB_PERFORMANCE_MODE_LOWNOISE:
 602		*profile = PLATFORM_PROFILE_QUIET;
 603		break;
 604	case GB_PERFORMANCE_MODE_OPTIMIZED:
 605	case GB_PERFORMANCE_MODE_OPTIMIZED_V2:
 606		*profile = PLATFORM_PROFILE_BALANCED;
 607		break;
 608	case GB_PERFORMANCE_MODE_PERFORMANCE:
 609	case GB_PERFORMANCE_MODE_PERFORMANCE_V2:
 610	case GB_PERFORMANCE_MODE_ULTRA:
 611		*profile = PLATFORM_PROFILE_PERFORMANCE;
 612		break;
 613	case GB_PERFORMANCE_MODE_IGNORE1:
 614	case GB_PERFORMANCE_MODE_IGNORE2:
 615		return -EOPNOTSUPP;
 616	default:
 617		dev_warn(&galaxybook->platform->dev,
 618			 "unrecognized performance mode 0x%x\n", performance_mode);
 619		return -EOPNOTSUPP;
 620	}
 621
 622	return 0;
 623}
 624
 625static int galaxybook_platform_profile_get(struct device *dev,
 626					   enum platform_profile_option *profile)
 627{
 628	struct samsung_galaxybook *galaxybook = dev_get_drvdata(dev);
 629	u8 performance_mode;
 630	int err;
 631
 632	err = performance_mode_acpi_get(galaxybook, &performance_mode);
 633	if (err)
 634		return err;
 635
 636	return get_performance_mode_profile(galaxybook, performance_mode, profile);
 637}
 638
 639static int galaxybook_platform_profile_set(struct device *dev,
 640					   enum platform_profile_option profile)
 641{
 642	struct samsung_galaxybook *galaxybook = dev_get_drvdata(dev);
 643
 644	return performance_mode_acpi_set(galaxybook,
 645					 galaxybook->profile_performance_modes[profile]);
 646}
 647
 648static int galaxybook_platform_profile_probe(void *drvdata, unsigned long *choices)
 649{
 650	struct samsung_galaxybook *galaxybook = drvdata;
 651	u8 *perfmodes = galaxybook->profile_performance_modes;
 652	enum platform_profile_option profile;
 653	struct sawb buf = {};
 654	unsigned int i;
 655	int err;
 656
 657	buf.safn = GB_SAFN;
 658	buf.sasb = GB_SASB_PERFORMANCE_MODE;
 659	export_guid(buf.caid, &GB_PERFORMANCE_MODE_GUID);
 660	buf.fncn = GB_FNCN_PERFORMANCE_MODE;
 661	buf.subn = GB_SUBN_PERFORMANCE_MODE_LIST;
 662
 663	err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_PERFORMANCE_MODE,
 664				     &buf, GB_SAWB_LEN_PERFORMANCE_MODE);
 665	if (err) {
 666		dev_dbg(&galaxybook->platform->dev,
 667			"failed to get supported performance modes, error %d\n", err);
 668		return err;
 669	}
 670
 671	/* set initial default profile performance mode values */
 672	perfmodes[PLATFORM_PROFILE_LOW_POWER] = GB_PERFORMANCE_MODE_FANOFF;
 673	perfmodes[PLATFORM_PROFILE_QUIET] = GB_PERFORMANCE_MODE_LOWNOISE;
 674	perfmodes[PLATFORM_PROFILE_BALANCED] = GB_PERFORMANCE_MODE_OPTIMIZED;
 675	perfmodes[PLATFORM_PROFILE_PERFORMANCE] = GB_PERFORMANCE_MODE_PERFORMANCE;
 676
 677	/*
 678	 * Value returned in iob0 will have the number of supported performance
 679	 * modes per device. The performance mode values will then be given as a
 680	 * list after this (iob1-iobX). Loop through the supported values and
 681	 * enable their mapped platform_profile choice, overriding "legacy"
 682	 * values along the way if a non-legacy value exists.
 683	 */
 684	for (i = 1; i <= buf.iob0; i++) {
 685		err = get_performance_mode_profile(galaxybook, buf.iobs[i], &profile);
 686		if (err) {
 687			dev_dbg(&galaxybook->platform->dev,
 688				"ignoring unmapped performance mode 0x%x\n", buf.iobs[i]);
 689			continue;
 690		}
 691		switch (buf.iobs[i]) {
 692		case GB_PERFORMANCE_MODE_OPTIMIZED_V2:
 693			perfmodes[profile] = GB_PERFORMANCE_MODE_OPTIMIZED_V2;
 694			break;
 695		case GB_PERFORMANCE_MODE_PERFORMANCE_V2:
 696			/* only update if not already overwritten by Ultra */
 697			if (perfmodes[profile] != GB_PERFORMANCE_MODE_ULTRA)
 698				perfmodes[profile] = GB_PERFORMANCE_MODE_PERFORMANCE_V2;
 699			break;
 700		case GB_PERFORMANCE_MODE_ULTRA:
 701			perfmodes[profile] = GB_PERFORMANCE_MODE_ULTRA;
 702			break;
 703		default:
 704			break;
 705		}
 706		set_bit(profile, choices);
 707		dev_dbg(&galaxybook->platform->dev,
 708			"setting platform profile %d to use performance mode 0x%x\n",
 709			profile, perfmodes[profile]);
 710	}
 711
 712	/* initialize performance_mode using balanced's mapped value */
 713	if (test_bit(PLATFORM_PROFILE_BALANCED, choices))
 714		return performance_mode_acpi_set(galaxybook, perfmodes[PLATFORM_PROFILE_BALANCED]);
 715
 716	return 0;
 717}
 718
 719static const struct platform_profile_ops galaxybook_platform_profile_ops = {
 720	.probe = galaxybook_platform_profile_probe,
 721	.profile_get = galaxybook_platform_profile_get,
 722	.profile_set = galaxybook_platform_profile_set,
 723};
 724
 725static int galaxybook_platform_profile_init(struct samsung_galaxybook *galaxybook)
 726{
 727	struct device *platform_profile_dev;
 728	u8 performance_mode;
 729	int err;
 730
 731	err = performance_mode_acpi_get(galaxybook, &performance_mode);
 732	if (err) {
 733		dev_dbg(&galaxybook->platform->dev,
 734			"failed to get initial performance mode, error %d\n", err);
 735		return GB_NOT_SUPPORTED;
 736	}
 737
 738	platform_profile_dev = devm_platform_profile_register(&galaxybook->platform->dev,
 739							      DRIVER_NAME, galaxybook,
 740							      &galaxybook_platform_profile_ops);
 741
 742	return PTR_ERR_OR_ZERO(platform_profile_dev);
 743}
 744
 745/*
 746 * Firmware Attributes
 747 */
 748
 749/* Power on lid open (device should power on when lid is opened) */
 750
 751static int power_on_lid_open_acpi_get(struct samsung_galaxybook *galaxybook, bool *value)
 752{
 753	struct sawb buf = {};
 754	int err;
 755
 756	buf.safn = GB_SAFN;
 757	buf.sasb = GB_SASB_POWER_MANAGEMENT;
 758	buf.gunm = GB_GUNM_POWER_MANAGEMENT;
 759	buf.guds[0] = GB_GUDS_POWER_ON_LID_OPEN;
 760	buf.guds[1] = GB_GUDS_POWER_ON_LID_OPEN_GET;
 761
 762	err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
 763				     &buf, GB_SAWB_LEN_SETTINGS);
 764	if (err)
 765		return err;
 766
 767	*value = buf.guds[1];
 768
 769	return 0;
 770}
 771
 772static int power_on_lid_open_acpi_set(struct samsung_galaxybook *galaxybook, const bool value)
 773{
 774	struct sawb buf = {};
 775
 776	lockdep_assert_held(&galaxybook->fw_attr_lock);
 777
 778	buf.safn = GB_SAFN;
 779	buf.sasb = GB_SASB_POWER_MANAGEMENT;
 780	buf.gunm = GB_GUNM_POWER_MANAGEMENT;
 781	buf.guds[0] = GB_GUDS_POWER_ON_LID_OPEN;
 782	buf.guds[1] = GB_GUDS_POWER_ON_LID_OPEN_SET;
 783	buf.guds[2] = value ? 1 : 0;
 784
 785	return galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
 786				      &buf, GB_SAWB_LEN_SETTINGS);
 787}
 788
 789/* USB Charging (USB ports can provide power when device is powered off) */
 790
 791static int usb_charging_acpi_get(struct samsung_galaxybook *galaxybook, bool *value)
 792{
 793	struct sawb buf = {};
 794	int err;
 795
 796	buf.safn = GB_SAFN;
 797	buf.sasb = GB_SASB_USB_CHARGING_GET;
 798	buf.gunm = GB_GUNM_USB_CHARGING_GET;
 799
 800	err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
 801				     &buf, GB_SAWB_LEN_SETTINGS);
 802	if (err)
 803		return err;
 804
 805	*value = buf.gunm == 1;
 806
 807	return 0;
 808}
 809
 810static int usb_charging_acpi_set(struct samsung_galaxybook *galaxybook, const bool value)
 811{
 812	struct sawb buf = {};
 813
 814	lockdep_assert_held(&galaxybook->fw_attr_lock);
 815
 816	buf.safn = GB_SAFN;
 817	buf.sasb = GB_SASB_USB_CHARGING_SET;
 818	buf.gunm = value ? GB_GUNM_USB_CHARGING_ON : GB_GUNM_USB_CHARGING_OFF;
 819
 820	return galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
 821				      &buf, GB_SAWB_LEN_SETTINGS);
 822}
 823
 824/* Block recording (blocks access to camera and microphone) */
 825
 826static int block_recording_acpi_get(struct samsung_galaxybook *galaxybook, bool *value)
 827{
 828	struct sawb buf = {};
 829	int err;
 830
 831	buf.safn = GB_SAFN;
 832	buf.sasb = GB_SASB_BLOCK_RECORDING;
 833	buf.gunm = GB_GUNM_GET;
 834
 835	err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
 836				     &buf, GB_SAWB_LEN_SETTINGS);
 837	if (err)
 838		return err;
 839
 840	*value = buf.gunm == GB_BLOCK_RECORDING_ON;
 841
 842	return 0;
 843}
 844
 845static int block_recording_acpi_set(struct samsung_galaxybook *galaxybook, const bool value)
 846{
 847	struct sawb buf = {};
 848	int err;
 849
 850	lockdep_assert_held(&galaxybook->fw_attr_lock);
 851
 852	buf.safn = GB_SAFN;
 853	buf.sasb = GB_SASB_BLOCK_RECORDING;
 854	buf.gunm = GB_GUNM_SET;
 855	buf.guds[0] = value ? GB_BLOCK_RECORDING_ON : GB_BLOCK_RECORDING_OFF;
 856
 857	err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
 858				     &buf, GB_SAWB_LEN_SETTINGS);
 859	if (err)
 860		return err;
 861
 862	input_report_switch(galaxybook->camera_lens_cover_switch,
 863			    SW_CAMERA_LENS_COVER, value ? 1 : 0);
 864	input_sync(galaxybook->camera_lens_cover_switch);
 865
 866	return 0;
 867}
 868
 869static int galaxybook_block_recording_init(struct samsung_galaxybook *galaxybook)
 870{
 871	bool value;
 872	int err;
 873
 874	err = galaxybook_enable_acpi_feature(galaxybook, GB_SASB_BLOCK_RECORDING);
 875	if (err) {
 876		dev_dbg(&galaxybook->platform->dev,
 877			"failed to initialize block_recording, error %d\n", err);
 878		return GB_NOT_SUPPORTED;
 879	}
 880
 881	guard(mutex)(&galaxybook->fw_attr_lock);
 882
 883	err = block_recording_acpi_get(galaxybook, &value);
 884	if (err) {
 885		dev_dbg(&galaxybook->platform->dev,
 886			"failed to get initial block_recording state, error %d\n", err);
 887		return GB_NOT_SUPPORTED;
 888	}
 889
 890	galaxybook->camera_lens_cover_switch =
 891		devm_input_allocate_device(&galaxybook->platform->dev);
 892	if (!galaxybook->camera_lens_cover_switch)
 893		return -ENOMEM;
 894
 895	galaxybook->camera_lens_cover_switch->name = "Samsung Galaxy Book Camera Lens Cover";
 896	galaxybook->camera_lens_cover_switch->phys = DRIVER_NAME "/input0";
 897	galaxybook->camera_lens_cover_switch->id.bustype = BUS_HOST;
 898
 899	input_set_capability(galaxybook->camera_lens_cover_switch, EV_SW, SW_CAMERA_LENS_COVER);
 900
 901	err = input_register_device(galaxybook->camera_lens_cover_switch);
 902	if (err)
 903		return err;
 904
 905	input_report_switch(galaxybook->camera_lens_cover_switch,
 906			    SW_CAMERA_LENS_COVER, value ? 1 : 0);
 907	input_sync(galaxybook->camera_lens_cover_switch);
 908
 909	return 0;
 910}
 911
 912/* Firmware Attributes setup */
 913
 914static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
 915{
 916	return sysfs_emit(buf, "enumeration\n");
 917}
 918
 919static struct kobj_attribute fw_attr_type = __ATTR_RO(type);
 920
 921static ssize_t default_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
 922{
 923	return sysfs_emit(buf, "0\n");
 924}
 925
 926static struct kobj_attribute fw_attr_default_value = __ATTR_RO(default_value);
 927
 928static ssize_t possible_values_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
 929{
 930	return sysfs_emit(buf, "0;1\n");
 931}
 932
 933static struct kobj_attribute fw_attr_possible_values = __ATTR_RO(possible_values);
 934
 935static ssize_t display_name_language_code_show(struct kobject *kobj, struct kobj_attribute *attr,
 936					       char *buf)
 937{
 938	return sysfs_emit(buf, "%s\n", GB_ATTR_LANGUAGE_CODE);
 939}
 940
 941static struct kobj_attribute fw_attr_display_name_language_code =
 942	__ATTR_RO(display_name_language_code);
 943
 944static ssize_t display_name_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
 945{
 946	struct galaxybook_fw_attr *fw_attr =
 947		container_of(attr, struct galaxybook_fw_attr, display_name);
 948
 949	return sysfs_emit(buf, "%s\n", galaxybook_fw_attr_desc[fw_attr->fw_attr_id]);
 950}
 951
 952static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
 953{
 954	struct galaxybook_fw_attr *fw_attr =
 955		container_of(attr, struct galaxybook_fw_attr, current_value);
 956	bool value;
 957	int err;
 958
 959	err = fw_attr->get_value(fw_attr->galaxybook, &value);
 960	if (err)
 961		return err;
 962
 963	return sysfs_emit(buf, "%u\n", value);
 964}
 965
 966static ssize_t current_value_store(struct kobject *kobj, struct kobj_attribute *attr,
 967				   const char *buf, size_t count)
 968{
 969	struct galaxybook_fw_attr *fw_attr =
 970		container_of(attr, struct galaxybook_fw_attr, current_value);
 971	struct samsung_galaxybook *galaxybook = fw_attr->galaxybook;
 972	bool value;
 973	int err;
 974
 975	if (!count)
 976		return -EINVAL;
 977
 978	err = kstrtobool(buf, &value);
 979	if (err)
 980		return err;
 981
 982	guard(mutex)(&galaxybook->fw_attr_lock);
 983
 984	err = fw_attr->set_value(galaxybook, value);
 985	if (err)
 986		return err;
 987
 988	return count;
 989}
 990
 991#define NUM_FW_ATTR_ENUM_ATTRS  6
 992
 993static int galaxybook_fw_attr_init(struct samsung_galaxybook *galaxybook,
 994				   const enum galaxybook_fw_attr_id fw_attr_id,
 995				   int (*get_value)(struct samsung_galaxybook *galaxybook,
 996						    bool *value),
 997				   int (*set_value)(struct samsung_galaxybook *galaxybook,
 998						    const bool value))
 999{
1000	struct galaxybook_fw_attr *fw_attr;
1001	struct attribute **attrs;
1002
1003	fw_attr = devm_kzalloc(&galaxybook->platform->dev, sizeof(*fw_attr), GFP_KERNEL);
1004	if (!fw_attr)
1005		return -ENOMEM;
1006
1007	attrs = devm_kcalloc(&galaxybook->platform->dev, NUM_FW_ATTR_ENUM_ATTRS + 1,
1008			     sizeof(*attrs), GFP_KERNEL);
1009	if (!attrs)
1010		return -ENOMEM;
1011
1012	attrs[0] = &fw_attr_type.attr;
1013	attrs[1] = &fw_attr_default_value.attr;
1014	attrs[2] = &fw_attr_possible_values.attr;
1015	attrs[3] = &fw_attr_display_name_language_code.attr;
1016
1017	sysfs_attr_init(&fw_attr->display_name.attr);
1018	fw_attr->display_name.attr.name = "display_name";
1019	fw_attr->display_name.attr.mode = 0444;
1020	fw_attr->display_name.show = display_name_show;
1021	attrs[4] = &fw_attr->display_name.attr;
1022
1023	sysfs_attr_init(&fw_attr->current_value.attr);
1024	fw_attr->current_value.attr.name = "current_value";
1025	fw_attr->current_value.attr.mode = 0644;
1026	fw_attr->current_value.show = current_value_show;
1027	fw_attr->current_value.store = current_value_store;
1028	attrs[5] = &fw_attr->current_value.attr;
1029
1030	attrs[6] = NULL;
1031
1032	fw_attr->galaxybook = galaxybook;
1033	fw_attr->fw_attr_id = fw_attr_id;
1034	fw_attr->attr_group.name = galaxybook_fw_attr_name[fw_attr_id];
1035	fw_attr->attr_group.attrs = attrs;
1036	fw_attr->get_value = get_value;
1037	fw_attr->set_value = set_value;
1038
1039	return sysfs_create_group(&galaxybook->fw_attrs_kset->kobj, &fw_attr->attr_group);
1040}
1041
1042static void galaxybook_kset_unregister(void *data)
1043{
1044	struct kset *kset = data;
1045
1046	kset_unregister(kset);
1047}
1048
1049static void galaxybook_fw_attrs_dev_unregister(void *data)
1050{
1051	struct device *fw_attrs_dev = data;
1052
1053	device_unregister(fw_attrs_dev);
1054}
1055
1056static int galaxybook_fw_attrs_init(struct samsung_galaxybook *galaxybook)
1057{
1058	bool value;
1059	int err;
1060
1061	err = devm_mutex_init(&galaxybook->platform->dev, &galaxybook->fw_attr_lock);
1062	if (err)
1063		return err;
1064
1065	galaxybook->fw_attrs_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0),
1066						 NULL, "%s", DRIVER_NAME);
1067	if (IS_ERR(galaxybook->fw_attrs_dev))
1068		return PTR_ERR(galaxybook->fw_attrs_dev);
1069
1070	err = devm_add_action_or_reset(&galaxybook->platform->dev,
1071				       galaxybook_fw_attrs_dev_unregister,
1072				       galaxybook->fw_attrs_dev);
1073	if (err)
1074		return err;
1075
1076	galaxybook->fw_attrs_kset = kset_create_and_add("attributes", NULL,
1077							&galaxybook->fw_attrs_dev->kobj);
1078	if (!galaxybook->fw_attrs_kset)
1079		return -ENOMEM;
1080	err = devm_add_action_or_reset(&galaxybook->platform->dev,
1081				       galaxybook_kset_unregister, galaxybook->fw_attrs_kset);
1082	if (err)
1083		return err;
1084
1085	err = power_on_lid_open_acpi_get(galaxybook, &value);
1086	if (!err) {
1087		err = galaxybook_fw_attr_init(galaxybook,
1088					      GB_ATTR_POWER_ON_LID_OPEN,
1089					      &power_on_lid_open_acpi_get,
1090					      &power_on_lid_open_acpi_set);
1091		if (err)
1092			return err;
1093	}
1094
1095	err = usb_charging_acpi_get(galaxybook, &value);
1096	if (!err) {
1097		err = galaxybook_fw_attr_init(galaxybook,
1098					      GB_ATTR_USB_CHARGING,
1099					      &usb_charging_acpi_get,
1100					      &usb_charging_acpi_set);
1101		if (err)
1102			return err;
1103	}
1104
1105	err = galaxybook_block_recording_init(galaxybook);
1106	if (err == GB_NOT_SUPPORTED)
1107		return 0;
1108	else if (err)
1109		return err;
1110
1111	galaxybook->has_block_recording = true;
1112
1113	return galaxybook_fw_attr_init(galaxybook,
1114				       GB_ATTR_BLOCK_RECORDING,
1115				       &block_recording_acpi_get,
1116				       &block_recording_acpi_set);
1117}
1118
1119/*
1120 * Hotkeys and notifications
1121 */
1122
1123static void galaxybook_kbd_backlight_hotkey_work(struct work_struct *work)
1124{
1125	struct samsung_galaxybook *galaxybook =
1126		from_work(galaxybook, work, kbd_backlight_hotkey_work);
1127	int brightness;
1128	int err;
1129
1130	guard(mutex)(&galaxybook->kbd_backlight_lock);
1131
1132	brightness = galaxybook->kbd_backlight.brightness;
1133	if (brightness < galaxybook->kbd_backlight.max_brightness)
1134		brightness++;
1135	else
1136		brightness = 0;
1137
1138	err = led_set_brightness_sync(&galaxybook->kbd_backlight, brightness);
1139	if (err) {
1140		dev_err(&galaxybook->platform->dev,
1141			"failed to set kbd_backlight brightness, error %d\n", err);
1142		return;
1143	}
1144
1145	led_classdev_notify_brightness_hw_changed(&galaxybook->kbd_backlight, brightness);
1146}
1147
1148static void galaxybook_block_recording_hotkey_work(struct work_struct *work)
1149{
1150	struct samsung_galaxybook *galaxybook =
1151		from_work(galaxybook, work, block_recording_hotkey_work);
1152	bool value;
1153	int err;
1154
1155	guard(mutex)(&galaxybook->fw_attr_lock);
1156
1157	err = block_recording_acpi_get(galaxybook, &value);
1158	if (err) {
1159		dev_err(&galaxybook->platform->dev,
1160			"failed to get block_recording, error %d\n", err);
1161		return;
1162	}
1163
1164	err = block_recording_acpi_set(galaxybook, !value);
1165	if (err)
1166		dev_err(&galaxybook->platform->dev,
1167			"failed to set block_recording, error %d\n", err);
1168}
1169
1170static bool galaxybook_i8042_filter(unsigned char data, unsigned char str, struct serio *port,
1171				    void *context)
1172{
1173	struct samsung_galaxybook *galaxybook = context;
1174	static bool extended;
1175
1176	if (str & I8042_STR_AUXDATA)
1177		return false;
1178
1179	if (data == 0xe0) {
1180		extended = true;
1181		return true;
1182	} else if (extended) {
1183		extended = false;
1184		switch (data) {
1185		case GB_KEY_KBD_BACKLIGHT_KEYDOWN:
1186			return true;
1187		case GB_KEY_KBD_BACKLIGHT_KEYUP:
1188			if (galaxybook->has_kbd_backlight)
1189				schedule_work(&galaxybook->kbd_backlight_hotkey_work);
1190			return true;
1191
1192		case GB_KEY_BLOCK_RECORDING_KEYDOWN:
1193			return true;
1194		case GB_KEY_BLOCK_RECORDING_KEYUP:
1195			if (galaxybook->has_block_recording)
1196				schedule_work(&galaxybook->block_recording_hotkey_work);
1197			return true;
1198
1199		/* battery notification already sent to battery + SCAI device */
1200		case GB_KEY_BATTERY_NOTIFY_KEYUP:
1201		case GB_KEY_BATTERY_NOTIFY_KEYDOWN:
1202			return true;
1203
1204		default:
1205			/*
1206			 * Report the previously filtered e0 before continuing
1207			 * with the next non-filtered byte.
1208			 */
1209			serio_interrupt(port, 0xe0, 0);
1210			return false;
1211		}
1212	}
1213
1214	return false;
1215}
1216
1217static void galaxybook_i8042_filter_remove(void *data)
1218{
1219	struct samsung_galaxybook *galaxybook = data;
1220
1221	i8042_remove_filter(galaxybook_i8042_filter);
1222	cancel_work_sync(&galaxybook->kbd_backlight_hotkey_work);
1223	cancel_work_sync(&galaxybook->block_recording_hotkey_work);
1224}
1225
1226static int galaxybook_i8042_filter_install(struct samsung_galaxybook *galaxybook)
1227{
1228	int err;
1229
1230	if (!galaxybook->has_kbd_backlight && !galaxybook->has_block_recording)
1231		return 0;
1232
1233	INIT_WORK(&galaxybook->kbd_backlight_hotkey_work,
1234		  galaxybook_kbd_backlight_hotkey_work);
1235	INIT_WORK(&galaxybook->block_recording_hotkey_work,
1236		  galaxybook_block_recording_hotkey_work);
1237
1238	err = i8042_install_filter(galaxybook_i8042_filter, galaxybook);
1239	if (err)
1240		return err;
1241
1242	return devm_add_action_or_reset(&galaxybook->platform->dev,
1243					galaxybook_i8042_filter_remove, galaxybook);
1244}
1245
1246/*
1247 * ACPI device setup
1248 */
1249
1250static void galaxybook_acpi_notify(acpi_handle handle, u32 event, void *data)
1251{
1252	struct samsung_galaxybook *galaxybook = data;
1253
1254	switch (event) {
1255	case GB_ACPI_NOTIFY_BATTERY_STATE_CHANGED:
1256	case GB_ACPI_NOTIFY_DEVICE_ON_TABLE:
1257	case GB_ACPI_NOTIFY_DEVICE_OFF_TABLE:
1258		break;
1259	case GB_ACPI_NOTIFY_HOTKEY_PERFORMANCE_MODE:
1260		if (galaxybook->has_performance_mode)
1261			platform_profile_cycle();
1262		break;
1263	default:
1264		dev_warn(&galaxybook->platform->dev,
1265			 "unknown ACPI notification event: 0x%x\n", event);
1266	}
1267
1268	acpi_bus_generate_netlink_event(DRIVER_NAME, dev_name(&galaxybook->platform->dev),
1269					event, 1);
1270}
1271
1272static int galaxybook_enable_acpi_notify(struct samsung_galaxybook *galaxybook)
1273{
1274	struct sawb buf = {};
1275	int err;
1276
1277	err = galaxybook_enable_acpi_feature(galaxybook, GB_SASB_NOTIFICATIONS);
1278	if (err)
1279		return err;
1280
1281	buf.safn = GB_SAFN;
1282	buf.sasb = GB_SASB_NOTIFICATIONS;
1283	buf.gunm = GB_GUNM_ACPI_NOTIFY_ENABLE;
1284	buf.guds[0] = GB_GUDS_ACPI_NOTIFY_ENABLE;
1285
1286	return galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
1287				      &buf, GB_SAWB_LEN_SETTINGS);
1288}
1289
1290static void galaxybook_acpi_remove_notify_handler(void *data)
1291{
1292	struct samsung_galaxybook *galaxybook = data;
1293
1294	acpi_remove_notify_handler(galaxybook->acpi->handle, ACPI_ALL_NOTIFY,
1295				   galaxybook_acpi_notify);
1296}
1297
1298static void galaxybook_acpi_disable(void *data)
1299{
1300	struct samsung_galaxybook *galaxybook = data;
1301
1302	acpi_execute_simple_method(galaxybook->acpi->handle,
1303				   GB_ACPI_METHOD_ENABLE, GB_ACPI_METHOD_ENABLE_OFF);
1304}
1305
1306static int galaxybook_acpi_init(struct samsung_galaxybook *galaxybook)
1307{
1308	acpi_status status;
1309	int err;
1310
1311	status = acpi_execute_simple_method(galaxybook->acpi->handle, GB_ACPI_METHOD_ENABLE,
1312					    GB_ACPI_METHOD_ENABLE_ON);
1313	if (ACPI_FAILURE(status))
1314		return -EIO;
1315	err = devm_add_action_or_reset(&galaxybook->platform->dev,
1316				       galaxybook_acpi_disable, galaxybook);
1317	if (err)
1318		return err;
1319
1320	status = acpi_install_notify_handler(galaxybook->acpi->handle, ACPI_ALL_NOTIFY,
1321					     galaxybook_acpi_notify, galaxybook);
1322	if (ACPI_FAILURE(status))
1323		return -EIO;
1324	err = devm_add_action_or_reset(&galaxybook->platform->dev,
1325				       galaxybook_acpi_remove_notify_handler, galaxybook);
1326	if (err)
1327		return err;
1328
1329	err = galaxybook_enable_acpi_notify(galaxybook);
1330	if (err)
1331		dev_dbg(&galaxybook->platform->dev, "failed to enable ACPI notifications; "
1332			"some hotkeys will not be supported\n");
1333
1334	err = galaxybook_enable_acpi_feature(galaxybook, GB_SASB_POWER_MANAGEMENT);
1335	if (err)
1336		dev_dbg(&galaxybook->platform->dev,
1337			"failed to initialize ACPI power management features; "
1338			"many features of this driver will not be available\n");
1339
1340	return 0;
1341}
1342
1343/*
1344 * Platform driver
1345 */
1346
1347static int galaxybook_probe(struct platform_device *pdev)
1348{
1349	struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
1350	struct samsung_galaxybook *galaxybook;
1351	int err;
1352
1353	if (!adev)
1354		return -ENODEV;
1355
1356	galaxybook = devm_kzalloc(&pdev->dev, sizeof(*galaxybook), GFP_KERNEL);
1357	if (!galaxybook)
1358		return -ENOMEM;
1359
1360	galaxybook->platform = pdev;
1361	galaxybook->acpi = adev;
1362
1363	/*
1364	 * Features must be enabled and initialized in the following order to
1365	 * avoid failures seen on certain devices:
1366	 * - GB_SASB_POWER_MANAGEMENT (including performance mode)
1367	 * - GB_SASB_KBD_BACKLIGHT
1368	 * - GB_SASB_BLOCK_RECORDING (as part of fw_attrs init)
1369	 */
1370
1371	err = galaxybook_acpi_init(galaxybook);
1372	if (err)
1373		return dev_err_probe(&galaxybook->platform->dev, err,
1374				     "failed to initialize ACPI device\n");
1375
1376	err = galaxybook_platform_profile_init(galaxybook);
1377	if (!err)
1378		galaxybook->has_performance_mode = true;
1379	else if (err != GB_NOT_SUPPORTED)
1380		return dev_err_probe(&galaxybook->platform->dev, err,
1381				     "failed to initialize platform profile\n");
1382
1383	err = galaxybook_battery_threshold_init(galaxybook);
1384	if (err)
1385		return dev_err_probe(&galaxybook->platform->dev, err,
1386				     "failed to initialize battery threshold\n");
1387
1388	err = galaxybook_kbd_backlight_init(galaxybook);
1389	if (!err)
1390		galaxybook->has_kbd_backlight = true;
1391	else if (err != GB_NOT_SUPPORTED)
1392		return dev_err_probe(&galaxybook->platform->dev, err,
1393				     "failed to initialize kbd_backlight\n");
1394
1395	err = galaxybook_fw_attrs_init(galaxybook);
1396	if (err)
1397		return dev_err_probe(&galaxybook->platform->dev, err,
1398				     "failed to initialize firmware-attributes\n");
1399
1400	err = galaxybook_i8042_filter_install(galaxybook);
1401	if (err)
1402		return dev_err_probe(&galaxybook->platform->dev, err,
1403				     "failed to initialize i8042_filter\n");
1404
1405	return 0;
1406}
1407
1408static const struct acpi_device_id galaxybook_device_ids[] = {
1409	{ "SAM0426" },
1410	{ "SAM0427" },
1411	{ "SAM0428" },
1412	{ "SAM0429" },
1413	{ "SAM0430" },
1414	{}
1415};
1416MODULE_DEVICE_TABLE(acpi, galaxybook_device_ids);
1417
1418static struct platform_driver galaxybook_platform_driver = {
1419	.driver = {
1420		.name = DRIVER_NAME,
1421		.acpi_match_table = galaxybook_device_ids,
1422	},
1423	.probe = galaxybook_probe,
1424};
1425module_platform_driver(galaxybook_platform_driver);
1426
1427MODULE_AUTHOR("Joshua Grisham <josh@joshuagrisham.com>");
1428MODULE_DESCRIPTION("Samsung Galaxy Book driver");
1429MODULE_LICENSE("GPL");
~/ Read-only file preview.