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 *camera_lens_cover_switch; 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 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 220 static 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 272 out_free: 273 kfree(out_obj); 274 return err; 275 } 276 277 static 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 303 static 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 323 static 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 338 static 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 352 static 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 361 static 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 402 static 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 423 static 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 438 static 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 467 static 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 494 static 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 505 static const enum power_supply_property galaxybook_battery_properties[] = { 506 POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD, 507 }; 508 509 static 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 518 static 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 527 static 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 533 static 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 556 static 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 577 static 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 593 static 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 625 static 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 639 static 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 648 static 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 719 static 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 725 static 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 751 static 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 772 static 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 791 static 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 810 static 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 826 static 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 845 static 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 869 static 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 914 static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 915 { 916 return sysfs_emit(buf, "enumeration\n"); 917 } 918 919 static struct kobj_attribute fw_attr_type = __ATTR_RO(type); 920 921 static ssize_t default_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 922 { 923 return sysfs_emit(buf, "0\n"); 924 } 925 926 static struct kobj_attribute fw_attr_default_value = __ATTR_RO(default_value); 927 928 static 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 933 static struct kobj_attribute fw_attr_possible_values = __ATTR_RO(possible_values); 934 935 static 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 941 static struct kobj_attribute fw_attr_display_name_language_code = 942 __ATTR_RO(display_name_language_code); 943 944 static 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 952 static 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 966 static 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 993 static 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 1042 static void galaxybook_kset_unregister(void *data) 1043 { 1044 struct kset *kset = data; 1045 1046 kset_unregister(kset); 1047 } 1048 1049 static 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 1056 static 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 1123 static 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 1148 static 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 1170 static 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 1217 static 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 1226 static 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 1250 static 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 1272 static 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 1290 static 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 1298 static 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 1306 static 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 1347 static 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 1408 static const struct acpi_device_id galaxybook_device_ids[] = { 1409 { "SAM0426" }, 1410 { "SAM0427" }, 1411 { "SAM0428" }, 1412 { "SAM0429" }, 1413 { "SAM0430" }, 1414 {} 1415 }; 1416 MODULE_DEVICE_TABLE(acpi, galaxybook_device_ids); 1417 1418 static 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 }; 1425 module_platform_driver(galaxybook_platform_driver); 1426 1427 MODULE_AUTHOR("Joshua Grisham <josh@joshuagrisham.com>"); 1428 MODULE_DESCRIPTION("Samsung Galaxy Book driver"); 1429 MODULE_LICENSE("GPL"); 1430