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