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 int err; 446 447 if (psp != POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD) 448 return -EINVAL; 449 450 err = charge_control_end_threshold_acpi_get(galaxybook, (u8 *)&val->intval); 451 if (err) 452 return err; 453 454 /* 455 * device stores "no end threshold" as 0 instead of 100; 456 * if device has 0, report 100 457 */ 458 if (val->intval == 0) 459 val->intval = 100; 460 461 return 0; 462 } 463 464 static int galaxybook_battery_ext_property_set(struct power_supply *psy, 465 const struct power_supply_ext *ext, 466 void *ext_data, 467 enum power_supply_property psp, 468 const union power_supply_propval *val) 469 { 470 struct samsung_galaxybook *galaxybook = ext_data; 471 u8 value; 472 473 if (psp != POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD) 474 return -EINVAL; 475 476 value = val->intval; 477 478 if (value < 1 || value > 100) 479 return -EINVAL; 480 481 /* 482 * device stores "no end threshold" as 0 instead of 100; 483 * if setting to 100, send 0 484 */ 485 if (value == 100) 486 value = 0; 487 488 return charge_control_end_threshold_acpi_set(galaxybook, value); 489 } 490 491 static int galaxybook_battery_ext_property_is_writeable(struct power_supply *psy, 492 const struct power_supply_ext *ext, 493 void *ext_data, 494 enum power_supply_property psp) 495 { 496 if (psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD) 497 return true; 498 499 return false; 500 } 501 502 static const enum power_supply_property galaxybook_battery_properties[] = { 503 POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD, 504 }; 505 506 static const struct power_supply_ext galaxybook_battery_ext = { 507 .name = DRIVER_NAME, 508 .properties = galaxybook_battery_properties, 509 .num_properties = ARRAY_SIZE(galaxybook_battery_properties), 510 .get_property = galaxybook_battery_ext_property_get, 511 .set_property = galaxybook_battery_ext_property_set, 512 .property_is_writeable = galaxybook_battery_ext_property_is_writeable, 513 }; 514 515 static int galaxybook_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook) 516 { 517 struct samsung_galaxybook *galaxybook = 518 container_of(hook, struct samsung_galaxybook, battery_hook); 519 520 return power_supply_register_extension(battery, &galaxybook_battery_ext, 521 &battery->dev, galaxybook); 522 } 523 524 static int galaxybook_battery_remove(struct power_supply *battery, struct acpi_battery_hook *hook) 525 { 526 power_supply_unregister_extension(battery, &galaxybook_battery_ext); 527 return 0; 528 } 529 530 static int galaxybook_battery_threshold_init(struct samsung_galaxybook *galaxybook) 531 { 532 u8 value; 533 int err; 534 535 err = charge_control_end_threshold_acpi_get(galaxybook, &value); 536 if (err) { 537 dev_dbg(&galaxybook->platform->dev, 538 "failed to get initial battery charge end threshold, error %d\n", err); 539 return 0; 540 } 541 542 galaxybook->battery_hook.add_battery = galaxybook_battery_add; 543 galaxybook->battery_hook.remove_battery = galaxybook_battery_remove; 544 galaxybook->battery_hook.name = "Samsung Galaxy Book Battery Extension"; 545 546 return devm_battery_hook_register(&galaxybook->platform->dev, &galaxybook->battery_hook); 547 } 548 549 /* 550 * Platform Profile / Performance mode 551 */ 552 553 static int performance_mode_acpi_get(struct samsung_galaxybook *galaxybook, u8 *performance_mode) 554 { 555 struct sawb buf = {}; 556 int err; 557 558 buf.safn = GB_SAFN; 559 buf.sasb = GB_SASB_PERFORMANCE_MODE; 560 export_guid(buf.caid, &GB_PERFORMANCE_MODE_GUID); 561 buf.fncn = GB_FNCN_PERFORMANCE_MODE; 562 buf.subn = GB_SUBN_PERFORMANCE_MODE_GET; 563 564 err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_PERFORMANCE_MODE, 565 &buf, GB_SAWB_LEN_PERFORMANCE_MODE); 566 if (err) 567 return err; 568 569 *performance_mode = buf.iob0; 570 571 return 0; 572 } 573 574 static int performance_mode_acpi_set(struct samsung_galaxybook *galaxybook, 575 const u8 performance_mode) 576 { 577 struct sawb buf = {}; 578 579 buf.safn = GB_SAFN; 580 buf.sasb = GB_SASB_PERFORMANCE_MODE; 581 export_guid(buf.caid, &GB_PERFORMANCE_MODE_GUID); 582 buf.fncn = GB_FNCN_PERFORMANCE_MODE; 583 buf.subn = GB_SUBN_PERFORMANCE_MODE_SET; 584 buf.iob0 = performance_mode; 585 586 return galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_PERFORMANCE_MODE, 587 &buf, GB_SAWB_LEN_PERFORMANCE_MODE); 588 } 589 590 static int get_performance_mode_profile(struct samsung_galaxybook *galaxybook, 591 const u8 performance_mode, 592 enum platform_profile_option *profile) 593 { 594 switch (performance_mode) { 595 case GB_PERFORMANCE_MODE_FANOFF: 596 *profile = PLATFORM_PROFILE_LOW_POWER; 597 break; 598 case GB_PERFORMANCE_MODE_LOWNOISE: 599 *profile = PLATFORM_PROFILE_QUIET; 600 break; 601 case GB_PERFORMANCE_MODE_OPTIMIZED: 602 case GB_PERFORMANCE_MODE_OPTIMIZED_V2: 603 *profile = PLATFORM_PROFILE_BALANCED; 604 break; 605 case GB_PERFORMANCE_MODE_PERFORMANCE: 606 case GB_PERFORMANCE_MODE_PERFORMANCE_V2: 607 case GB_PERFORMANCE_MODE_ULTRA: 608 *profile = PLATFORM_PROFILE_PERFORMANCE; 609 break; 610 case GB_PERFORMANCE_MODE_IGNORE1: 611 case GB_PERFORMANCE_MODE_IGNORE2: 612 return -EOPNOTSUPP; 613 default: 614 dev_warn(&galaxybook->platform->dev, 615 "unrecognized performance mode 0x%x\n", performance_mode); 616 return -EOPNOTSUPP; 617 } 618 619 return 0; 620 } 621 622 static int galaxybook_platform_profile_get(struct device *dev, 623 enum platform_profile_option *profile) 624 { 625 struct samsung_galaxybook *galaxybook = dev_get_drvdata(dev); 626 u8 performance_mode; 627 int err; 628 629 err = performance_mode_acpi_get(galaxybook, &performance_mode); 630 if (err) 631 return err; 632 633 return get_performance_mode_profile(galaxybook, performance_mode, profile); 634 } 635 636 static int galaxybook_platform_profile_set(struct device *dev, 637 enum platform_profile_option profile) 638 { 639 struct samsung_galaxybook *galaxybook = dev_get_drvdata(dev); 640 641 return performance_mode_acpi_set(galaxybook, 642 galaxybook->profile_performance_modes[profile]); 643 } 644 645 static int galaxybook_platform_profile_probe(void *drvdata, unsigned long *choices) 646 { 647 struct samsung_galaxybook *galaxybook = drvdata; 648 u8 *perfmodes = galaxybook->profile_performance_modes; 649 enum platform_profile_option profile; 650 struct sawb buf = {}; 651 unsigned int i; 652 int err; 653 654 buf.safn = GB_SAFN; 655 buf.sasb = GB_SASB_PERFORMANCE_MODE; 656 export_guid(buf.caid, &GB_PERFORMANCE_MODE_GUID); 657 buf.fncn = GB_FNCN_PERFORMANCE_MODE; 658 buf.subn = GB_SUBN_PERFORMANCE_MODE_LIST; 659 660 err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_PERFORMANCE_MODE, 661 &buf, GB_SAWB_LEN_PERFORMANCE_MODE); 662 if (err) { 663 dev_dbg(&galaxybook->platform->dev, 664 "failed to get supported performance modes, error %d\n", err); 665 return err; 666 } 667 668 /* set initial default profile performance mode values */ 669 perfmodes[PLATFORM_PROFILE_LOW_POWER] = GB_PERFORMANCE_MODE_FANOFF; 670 perfmodes[PLATFORM_PROFILE_QUIET] = GB_PERFORMANCE_MODE_LOWNOISE; 671 perfmodes[PLATFORM_PROFILE_BALANCED] = GB_PERFORMANCE_MODE_OPTIMIZED; 672 perfmodes[PLATFORM_PROFILE_PERFORMANCE] = GB_PERFORMANCE_MODE_PERFORMANCE; 673 674 /* 675 * Value returned in iob0 will have the number of supported performance 676 * modes per device. The performance mode values will then be given as a 677 * list after this (iob1-iobX). Loop through the supported values and 678 * enable their mapped platform_profile choice, overriding "legacy" 679 * values along the way if a non-legacy value exists. 680 */ 681 for (i = 1; i <= buf.iob0; i++) { 682 err = get_performance_mode_profile(galaxybook, buf.iobs[i], &profile); 683 if (err) { 684 dev_dbg(&galaxybook->platform->dev, 685 "ignoring unmapped performance mode 0x%x\n", buf.iobs[i]); 686 continue; 687 } 688 switch (buf.iobs[i]) { 689 case GB_PERFORMANCE_MODE_OPTIMIZED_V2: 690 perfmodes[profile] = GB_PERFORMANCE_MODE_OPTIMIZED_V2; 691 break; 692 case GB_PERFORMANCE_MODE_PERFORMANCE_V2: 693 /* only update if not already overwritten by Ultra */ 694 if (perfmodes[profile] != GB_PERFORMANCE_MODE_ULTRA) 695 perfmodes[profile] = GB_PERFORMANCE_MODE_PERFORMANCE_V2; 696 break; 697 case GB_PERFORMANCE_MODE_ULTRA: 698 perfmodes[profile] = GB_PERFORMANCE_MODE_ULTRA; 699 break; 700 default: 701 break; 702 } 703 set_bit(profile, choices); 704 dev_dbg(&galaxybook->platform->dev, 705 "setting platform profile %d to use performance mode 0x%x\n", 706 profile, perfmodes[profile]); 707 } 708 709 /* initialize performance_mode using balanced's mapped value */ 710 if (test_bit(PLATFORM_PROFILE_BALANCED, choices)) 711 return performance_mode_acpi_set(galaxybook, perfmodes[PLATFORM_PROFILE_BALANCED]); 712 713 return 0; 714 } 715 716 static const struct platform_profile_ops galaxybook_platform_profile_ops = { 717 .probe = galaxybook_platform_profile_probe, 718 .profile_get = galaxybook_platform_profile_get, 719 .profile_set = galaxybook_platform_profile_set, 720 }; 721 722 static int galaxybook_platform_profile_init(struct samsung_galaxybook *galaxybook) 723 { 724 struct device *platform_profile_dev; 725 u8 performance_mode; 726 int err; 727 728 err = performance_mode_acpi_get(galaxybook, &performance_mode); 729 if (err) { 730 dev_dbg(&galaxybook->platform->dev, 731 "failed to get initial performance mode, error %d\n", err); 732 return GB_NOT_SUPPORTED; 733 } 734 735 platform_profile_dev = devm_platform_profile_register(&galaxybook->platform->dev, 736 DRIVER_NAME, galaxybook, 737 &galaxybook_platform_profile_ops); 738 739 return PTR_ERR_OR_ZERO(platform_profile_dev); 740 } 741 742 /* 743 * Firmware Attributes 744 */ 745 746 /* Power on lid open (device should power on when lid is opened) */ 747 748 static int power_on_lid_open_acpi_get(struct samsung_galaxybook *galaxybook, bool *value) 749 { 750 struct sawb buf = {}; 751 int err; 752 753 buf.safn = GB_SAFN; 754 buf.sasb = GB_SASB_POWER_MANAGEMENT; 755 buf.gunm = GB_GUNM_POWER_MANAGEMENT; 756 buf.guds[0] = GB_GUDS_POWER_ON_LID_OPEN; 757 buf.guds[1] = GB_GUDS_POWER_ON_LID_OPEN_GET; 758 759 err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS, 760 &buf, GB_SAWB_LEN_SETTINGS); 761 if (err) 762 return err; 763 764 *value = buf.guds[1]; 765 766 return 0; 767 } 768 769 static int power_on_lid_open_acpi_set(struct samsung_galaxybook *galaxybook, const bool value) 770 { 771 struct sawb buf = {}; 772 773 lockdep_assert_held(&galaxybook->fw_attr_lock); 774 775 buf.safn = GB_SAFN; 776 buf.sasb = GB_SASB_POWER_MANAGEMENT; 777 buf.gunm = GB_GUNM_POWER_MANAGEMENT; 778 buf.guds[0] = GB_GUDS_POWER_ON_LID_OPEN; 779 buf.guds[1] = GB_GUDS_POWER_ON_LID_OPEN_SET; 780 buf.guds[2] = value ? 1 : 0; 781 782 return galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS, 783 &buf, GB_SAWB_LEN_SETTINGS); 784 } 785 786 /* USB Charging (USB ports can provide power when device is powered off) */ 787 788 static int usb_charging_acpi_get(struct samsung_galaxybook *galaxybook, bool *value) 789 { 790 struct sawb buf = {}; 791 int err; 792 793 buf.safn = GB_SAFN; 794 buf.sasb = GB_SASB_USB_CHARGING_GET; 795 buf.gunm = GB_GUNM_USB_CHARGING_GET; 796 797 err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS, 798 &buf, GB_SAWB_LEN_SETTINGS); 799 if (err) 800 return err; 801 802 *value = buf.gunm == 1; 803 804 return 0; 805 } 806 807 static int usb_charging_acpi_set(struct samsung_galaxybook *galaxybook, const bool value) 808 { 809 struct sawb buf = {}; 810 811 lockdep_assert_held(&galaxybook->fw_attr_lock); 812 813 buf.safn = GB_SAFN; 814 buf.sasb = GB_SASB_USB_CHARGING_SET; 815 buf.gunm = value ? GB_GUNM_USB_CHARGING_ON : GB_GUNM_USB_CHARGING_OFF; 816 817 return galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS, 818 &buf, GB_SAWB_LEN_SETTINGS); 819 } 820 821 /* Block recording (blocks access to camera and microphone) */ 822 823 static int block_recording_acpi_get(struct samsung_galaxybook *galaxybook, bool *value) 824 { 825 struct sawb buf = {}; 826 int err; 827 828 buf.safn = GB_SAFN; 829 buf.sasb = GB_SASB_BLOCK_RECORDING; 830 buf.gunm = GB_GUNM_GET; 831 832 err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS, 833 &buf, GB_SAWB_LEN_SETTINGS); 834 if (err) 835 return err; 836 837 *value = buf.gunm == GB_BLOCK_RECORDING_ON; 838 839 return 0; 840 } 841 842 static int block_recording_acpi_set(struct samsung_galaxybook *galaxybook, const bool value) 843 { 844 struct sawb buf = {}; 845 int err; 846 847 lockdep_assert_held(&galaxybook->fw_attr_lock); 848 849 buf.safn = GB_SAFN; 850 buf.sasb = GB_SASB_BLOCK_RECORDING; 851 buf.gunm = GB_GUNM_SET; 852 buf.guds[0] = value ? GB_BLOCK_RECORDING_ON : GB_BLOCK_RECORDING_OFF; 853 854 err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS, 855 &buf, GB_SAWB_LEN_SETTINGS); 856 if (err) 857 return err; 858 859 input_report_switch(galaxybook->camera_lens_cover_switch, 860 SW_CAMERA_LENS_COVER, value ? 1 : 0); 861 input_sync(galaxybook->camera_lens_cover_switch); 862 863 return 0; 864 } 865 866 static int galaxybook_block_recording_init(struct samsung_galaxybook *galaxybook) 867 { 868 bool value; 869 int err; 870 871 err = galaxybook_enable_acpi_feature(galaxybook, GB_SASB_BLOCK_RECORDING); 872 if (err) { 873 dev_dbg(&galaxybook->platform->dev, 874 "failed to initialize block_recording, error %d\n", err); 875 return GB_NOT_SUPPORTED; 876 } 877 878 guard(mutex)(&galaxybook->fw_attr_lock); 879 880 err = block_recording_acpi_get(galaxybook, &value); 881 if (err) { 882 dev_dbg(&galaxybook->platform->dev, 883 "failed to get initial block_recording state, error %d\n", err); 884 return GB_NOT_SUPPORTED; 885 } 886 887 galaxybook->camera_lens_cover_switch = 888 devm_input_allocate_device(&galaxybook->platform->dev); 889 if (!galaxybook->camera_lens_cover_switch) 890 return -ENOMEM; 891 892 galaxybook->camera_lens_cover_switch->name = "Samsung Galaxy Book Camera Lens Cover"; 893 galaxybook->camera_lens_cover_switch->phys = DRIVER_NAME "/input0"; 894 galaxybook->camera_lens_cover_switch->id.bustype = BUS_HOST; 895 896 input_set_capability(galaxybook->camera_lens_cover_switch, EV_SW, SW_CAMERA_LENS_COVER); 897 898 err = input_register_device(galaxybook->camera_lens_cover_switch); 899 if (err) 900 return err; 901 902 input_report_switch(galaxybook->camera_lens_cover_switch, 903 SW_CAMERA_LENS_COVER, value ? 1 : 0); 904 input_sync(galaxybook->camera_lens_cover_switch); 905 906 return 0; 907 } 908 909 /* Firmware Attributes setup */ 910 911 static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 912 { 913 return sysfs_emit(buf, "enumeration\n"); 914 } 915 916 static struct kobj_attribute fw_attr_type = __ATTR_RO(type); 917 918 static ssize_t default_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 919 { 920 return sysfs_emit(buf, "0\n"); 921 } 922 923 static struct kobj_attribute fw_attr_default_value = __ATTR_RO(default_value); 924 925 static ssize_t possible_values_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 926 { 927 return sysfs_emit(buf, "0;1\n"); 928 } 929 930 static struct kobj_attribute fw_attr_possible_values = __ATTR_RO(possible_values); 931 932 static ssize_t display_name_language_code_show(struct kobject *kobj, struct kobj_attribute *attr, 933 char *buf) 934 { 935 return sysfs_emit(buf, "%s\n", GB_ATTR_LANGUAGE_CODE); 936 } 937 938 static struct kobj_attribute fw_attr_display_name_language_code = 939 __ATTR_RO(display_name_language_code); 940 941 static ssize_t display_name_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 942 { 943 struct galaxybook_fw_attr *fw_attr = 944 container_of(attr, struct galaxybook_fw_attr, display_name); 945 946 return sysfs_emit(buf, "%s\n", galaxybook_fw_attr_desc[fw_attr->fw_attr_id]); 947 } 948 949 static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 950 { 951 struct galaxybook_fw_attr *fw_attr = 952 container_of(attr, struct galaxybook_fw_attr, current_value); 953 bool value; 954 int err; 955 956 err = fw_attr->get_value(fw_attr->galaxybook, &value); 957 if (err) 958 return err; 959 960 return sysfs_emit(buf, "%u\n", value); 961 } 962 963 static ssize_t current_value_store(struct kobject *kobj, struct kobj_attribute *attr, 964 const char *buf, size_t count) 965 { 966 struct galaxybook_fw_attr *fw_attr = 967 container_of(attr, struct galaxybook_fw_attr, current_value); 968 struct samsung_galaxybook *galaxybook = fw_attr->galaxybook; 969 bool value; 970 int err; 971 972 if (!count) 973 return -EINVAL; 974 975 err = kstrtobool(buf, &value); 976 if (err) 977 return err; 978 979 guard(mutex)(&galaxybook->fw_attr_lock); 980 981 err = fw_attr->set_value(galaxybook, value); 982 if (err) 983 return err; 984 985 return count; 986 } 987 988 #define NUM_FW_ATTR_ENUM_ATTRS 6 989 990 static int galaxybook_fw_attr_init(struct samsung_galaxybook *galaxybook, 991 const enum galaxybook_fw_attr_id fw_attr_id, 992 int (*get_value)(struct samsung_galaxybook *galaxybook, 993 bool *value), 994 int (*set_value)(struct samsung_galaxybook *galaxybook, 995 const bool value)) 996 { 997 struct galaxybook_fw_attr *fw_attr; 998 struct attribute **attrs; 999 1000 fw_attr = devm_kzalloc(&galaxybook->platform->dev, sizeof(*fw_attr), GFP_KERNEL); 1001 if (!fw_attr) 1002 return -ENOMEM; 1003 1004 attrs = devm_kcalloc(&galaxybook->platform->dev, NUM_FW_ATTR_ENUM_ATTRS + 1, 1005 sizeof(*attrs), GFP_KERNEL); 1006 if (!attrs) 1007 return -ENOMEM; 1008 1009 attrs[0] = &fw_attr_type.attr; 1010 attrs[1] = &fw_attr_default_value.attr; 1011 attrs[2] = &fw_attr_possible_values.attr; 1012 attrs[3] = &fw_attr_display_name_language_code.attr; 1013 1014 sysfs_attr_init(&fw_attr->display_name.attr); 1015 fw_attr->display_name.attr.name = "display_name"; 1016 fw_attr->display_name.attr.mode = 0444; 1017 fw_attr->display_name.show = display_name_show; 1018 attrs[4] = &fw_attr->display_name.attr; 1019 1020 sysfs_attr_init(&fw_attr->current_value.attr); 1021 fw_attr->current_value.attr.name = "current_value"; 1022 fw_attr->current_value.attr.mode = 0644; 1023 fw_attr->current_value.show = current_value_show; 1024 fw_attr->current_value.store = current_value_store; 1025 attrs[5] = &fw_attr->current_value.attr; 1026 1027 attrs[6] = NULL; 1028 1029 fw_attr->galaxybook = galaxybook; 1030 fw_attr->fw_attr_id = fw_attr_id; 1031 fw_attr->attr_group.name = galaxybook_fw_attr_name[fw_attr_id]; 1032 fw_attr->attr_group.attrs = attrs; 1033 fw_attr->get_value = get_value; 1034 fw_attr->set_value = set_value; 1035 1036 return sysfs_create_group(&galaxybook->fw_attrs_kset->kobj, &fw_attr->attr_group); 1037 } 1038 1039 static void galaxybook_kset_unregister(void *data) 1040 { 1041 struct kset *kset = data; 1042 1043 kset_unregister(kset); 1044 } 1045 1046 static void galaxybook_fw_attrs_dev_unregister(void *data) 1047 { 1048 struct device *fw_attrs_dev = data; 1049 1050 device_unregister(fw_attrs_dev); 1051 } 1052 1053 static int galaxybook_fw_attrs_init(struct samsung_galaxybook *galaxybook) 1054 { 1055 bool value; 1056 int err; 1057 1058 err = devm_mutex_init(&galaxybook->platform->dev, &galaxybook->fw_attr_lock); 1059 if (err) 1060 return err; 1061 1062 galaxybook->fw_attrs_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0), 1063 NULL, "%s", DRIVER_NAME); 1064 if (IS_ERR(galaxybook->fw_attrs_dev)) 1065 return PTR_ERR(galaxybook->fw_attrs_dev); 1066 1067 err = devm_add_action_or_reset(&galaxybook->platform->dev, 1068 galaxybook_fw_attrs_dev_unregister, 1069 galaxybook->fw_attrs_dev); 1070 if (err) 1071 return err; 1072 1073 galaxybook->fw_attrs_kset = kset_create_and_add("attributes", NULL, 1074 &galaxybook->fw_attrs_dev->kobj); 1075 if (!galaxybook->fw_attrs_kset) 1076 return -ENOMEM; 1077 err = devm_add_action_or_reset(&galaxybook->platform->dev, 1078 galaxybook_kset_unregister, galaxybook->fw_attrs_kset); 1079 if (err) 1080 return err; 1081 1082 err = power_on_lid_open_acpi_get(galaxybook, &value); 1083 if (!err) { 1084 err = galaxybook_fw_attr_init(galaxybook, 1085 GB_ATTR_POWER_ON_LID_OPEN, 1086 &power_on_lid_open_acpi_get, 1087 &power_on_lid_open_acpi_set); 1088 if (err) 1089 return err; 1090 } 1091 1092 err = usb_charging_acpi_get(galaxybook, &value); 1093 if (!err) { 1094 err = galaxybook_fw_attr_init(galaxybook, 1095 GB_ATTR_USB_CHARGING, 1096 &usb_charging_acpi_get, 1097 &usb_charging_acpi_set); 1098 if (err) 1099 return err; 1100 } 1101 1102 err = galaxybook_block_recording_init(galaxybook); 1103 if (err == GB_NOT_SUPPORTED) 1104 return 0; 1105 else if (err) 1106 return err; 1107 1108 galaxybook->has_block_recording = true; 1109 1110 return galaxybook_fw_attr_init(galaxybook, 1111 GB_ATTR_BLOCK_RECORDING, 1112 &block_recording_acpi_get, 1113 &block_recording_acpi_set); 1114 } 1115 1116 /* 1117 * Hotkeys and notifications 1118 */ 1119 1120 static void galaxybook_kbd_backlight_hotkey_work(struct work_struct *work) 1121 { 1122 struct samsung_galaxybook *galaxybook = 1123 from_work(galaxybook, work, kbd_backlight_hotkey_work); 1124 int brightness; 1125 int err; 1126 1127 guard(mutex)(&galaxybook->kbd_backlight_lock); 1128 1129 brightness = galaxybook->kbd_backlight.brightness; 1130 if (brightness < galaxybook->kbd_backlight.max_brightness) 1131 brightness++; 1132 else 1133 brightness = 0; 1134 1135 err = led_set_brightness_sync(&galaxybook->kbd_backlight, brightness); 1136 if (err) { 1137 dev_err(&galaxybook->platform->dev, 1138 "failed to set kbd_backlight brightness, error %d\n", err); 1139 return; 1140 } 1141 1142 led_classdev_notify_brightness_hw_changed(&galaxybook->kbd_backlight, brightness); 1143 } 1144 1145 static void galaxybook_block_recording_hotkey_work(struct work_struct *work) 1146 { 1147 struct samsung_galaxybook *galaxybook = 1148 from_work(galaxybook, work, block_recording_hotkey_work); 1149 bool value; 1150 int err; 1151 1152 guard(mutex)(&galaxybook->fw_attr_lock); 1153 1154 err = block_recording_acpi_get(galaxybook, &value); 1155 if (err) { 1156 dev_err(&galaxybook->platform->dev, 1157 "failed to get block_recording, error %d\n", err); 1158 return; 1159 } 1160 1161 err = block_recording_acpi_set(galaxybook, !value); 1162 if (err) 1163 dev_err(&galaxybook->platform->dev, 1164 "failed to set block_recording, error %d\n", err); 1165 } 1166 1167 static bool galaxybook_i8042_filter(unsigned char data, unsigned char str, struct serio *port, 1168 void *context) 1169 { 1170 struct samsung_galaxybook *galaxybook = context; 1171 static bool extended; 1172 1173 if (str & I8042_STR_AUXDATA) 1174 return false; 1175 1176 if (data == 0xe0) { 1177 extended = true; 1178 return true; 1179 } else if (extended) { 1180 extended = false; 1181 switch (data) { 1182 case GB_KEY_KBD_BACKLIGHT_KEYDOWN: 1183 return true; 1184 case GB_KEY_KBD_BACKLIGHT_KEYUP: 1185 if (galaxybook->has_kbd_backlight) 1186 schedule_work(&galaxybook->kbd_backlight_hotkey_work); 1187 return true; 1188 1189 case GB_KEY_BLOCK_RECORDING_KEYDOWN: 1190 return true; 1191 case GB_KEY_BLOCK_RECORDING_KEYUP: 1192 if (galaxybook->has_block_recording) 1193 schedule_work(&galaxybook->block_recording_hotkey_work); 1194 return true; 1195 1196 /* battery notification already sent to battery + SCAI device */ 1197 case GB_KEY_BATTERY_NOTIFY_KEYUP: 1198 case GB_KEY_BATTERY_NOTIFY_KEYDOWN: 1199 return true; 1200 1201 default: 1202 /* 1203 * Report the previously filtered e0 before continuing 1204 * with the next non-filtered byte. 1205 */ 1206 serio_interrupt(port, 0xe0, 0); 1207 return false; 1208 } 1209 } 1210 1211 return false; 1212 } 1213 1214 static void galaxybook_i8042_filter_remove(void *data) 1215 { 1216 struct samsung_galaxybook *galaxybook = data; 1217 1218 i8042_remove_filter(galaxybook_i8042_filter); 1219 cancel_work_sync(&galaxybook->kbd_backlight_hotkey_work); 1220 cancel_work_sync(&galaxybook->block_recording_hotkey_work); 1221 } 1222 1223 static int galaxybook_i8042_filter_install(struct samsung_galaxybook *galaxybook) 1224 { 1225 int err; 1226 1227 if (!galaxybook->has_kbd_backlight && !galaxybook->has_block_recording) 1228 return 0; 1229 1230 INIT_WORK(&galaxybook->kbd_backlight_hotkey_work, 1231 galaxybook_kbd_backlight_hotkey_work); 1232 INIT_WORK(&galaxybook->block_recording_hotkey_work, 1233 galaxybook_block_recording_hotkey_work); 1234 1235 err = i8042_install_filter(galaxybook_i8042_filter, galaxybook); 1236 if (err) 1237 return err; 1238 1239 return devm_add_action_or_reset(&galaxybook->platform->dev, 1240 galaxybook_i8042_filter_remove, galaxybook); 1241 } 1242 1243 /* 1244 * ACPI device setup 1245 */ 1246 1247 static void galaxybook_acpi_notify(acpi_handle handle, u32 event, void *data) 1248 { 1249 struct samsung_galaxybook *galaxybook = data; 1250 1251 switch (event) { 1252 case GB_ACPI_NOTIFY_BATTERY_STATE_CHANGED: 1253 case GB_ACPI_NOTIFY_DEVICE_ON_TABLE: 1254 case GB_ACPI_NOTIFY_DEVICE_OFF_TABLE: 1255 break; 1256 case GB_ACPI_NOTIFY_HOTKEY_PERFORMANCE_MODE: 1257 if (galaxybook->has_performance_mode) 1258 platform_profile_cycle(); 1259 break; 1260 default: 1261 dev_warn(&galaxybook->platform->dev, 1262 "unknown ACPI notification event: 0x%x\n", event); 1263 } 1264 1265 acpi_bus_generate_netlink_event(DRIVER_NAME, dev_name(&galaxybook->platform->dev), 1266 event, 1); 1267 } 1268 1269 static int galaxybook_enable_acpi_notify(struct samsung_galaxybook *galaxybook) 1270 { 1271 struct sawb buf = {}; 1272 int err; 1273 1274 err = galaxybook_enable_acpi_feature(galaxybook, GB_SASB_NOTIFICATIONS); 1275 if (err) 1276 return err; 1277 1278 buf.safn = GB_SAFN; 1279 buf.sasb = GB_SASB_NOTIFICATIONS; 1280 buf.gunm = GB_GUNM_ACPI_NOTIFY_ENABLE; 1281 buf.guds[0] = GB_GUDS_ACPI_NOTIFY_ENABLE; 1282 1283 return galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS, 1284 &buf, GB_SAWB_LEN_SETTINGS); 1285 } 1286 1287 static void galaxybook_acpi_remove_notify_handler(void *data) 1288 { 1289 struct samsung_galaxybook *galaxybook = data; 1290 1291 acpi_remove_notify_handler(galaxybook->acpi->handle, ACPI_ALL_NOTIFY, 1292 galaxybook_acpi_notify); 1293 } 1294 1295 static void galaxybook_acpi_disable(void *data) 1296 { 1297 struct samsung_galaxybook *galaxybook = data; 1298 1299 acpi_execute_simple_method(galaxybook->acpi->handle, 1300 GB_ACPI_METHOD_ENABLE, GB_ACPI_METHOD_ENABLE_OFF); 1301 } 1302 1303 static int galaxybook_acpi_init(struct samsung_galaxybook *galaxybook) 1304 { 1305 acpi_status status; 1306 int err; 1307 1308 status = acpi_execute_simple_method(galaxybook->acpi->handle, GB_ACPI_METHOD_ENABLE, 1309 GB_ACPI_METHOD_ENABLE_ON); 1310 if (ACPI_FAILURE(status)) 1311 return -EIO; 1312 err = devm_add_action_or_reset(&galaxybook->platform->dev, 1313 galaxybook_acpi_disable, galaxybook); 1314 if (err) 1315 return err; 1316 1317 status = acpi_install_notify_handler(galaxybook->acpi->handle, ACPI_ALL_NOTIFY, 1318 galaxybook_acpi_notify, galaxybook); 1319 if (ACPI_FAILURE(status)) 1320 return -EIO; 1321 err = devm_add_action_or_reset(&galaxybook->platform->dev, 1322 galaxybook_acpi_remove_notify_handler, galaxybook); 1323 if (err) 1324 return err; 1325 1326 err = galaxybook_enable_acpi_notify(galaxybook); 1327 if (err) 1328 dev_dbg(&galaxybook->platform->dev, "failed to enable ACPI notifications; " 1329 "some hotkeys will not be supported\n"); 1330 1331 err = galaxybook_enable_acpi_feature(galaxybook, GB_SASB_POWER_MANAGEMENT); 1332 if (err) 1333 dev_dbg(&galaxybook->platform->dev, 1334 "failed to initialize ACPI power management features; " 1335 "many features of this driver will not be available\n"); 1336 1337 return 0; 1338 } 1339 1340 /* 1341 * Platform driver 1342 */ 1343 1344 static int galaxybook_probe(struct platform_device *pdev) 1345 { 1346 struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); 1347 struct samsung_galaxybook *galaxybook; 1348 int err; 1349 1350 if (!adev) 1351 return -ENODEV; 1352 1353 galaxybook = devm_kzalloc(&pdev->dev, sizeof(*galaxybook), GFP_KERNEL); 1354 if (!galaxybook) 1355 return -ENOMEM; 1356 1357 galaxybook->platform = pdev; 1358 galaxybook->acpi = adev; 1359 1360 /* 1361 * Features must be enabled and initialized in the following order to 1362 * avoid failures seen on certain devices: 1363 * - GB_SASB_POWER_MANAGEMENT (including performance mode) 1364 * - GB_SASB_KBD_BACKLIGHT 1365 * - GB_SASB_BLOCK_RECORDING (as part of fw_attrs init) 1366 */ 1367 1368 err = galaxybook_acpi_init(galaxybook); 1369 if (err) 1370 return dev_err_probe(&galaxybook->platform->dev, err, 1371 "failed to initialize ACPI device\n"); 1372 1373 err = galaxybook_platform_profile_init(galaxybook); 1374 if (!err) 1375 galaxybook->has_performance_mode = true; 1376 else if (err != GB_NOT_SUPPORTED) 1377 return dev_err_probe(&galaxybook->platform->dev, err, 1378 "failed to initialize platform profile\n"); 1379 1380 err = galaxybook_battery_threshold_init(galaxybook); 1381 if (err) 1382 return dev_err_probe(&galaxybook->platform->dev, err, 1383 "failed to initialize battery threshold\n"); 1384 1385 err = galaxybook_kbd_backlight_init(galaxybook); 1386 if (!err) 1387 galaxybook->has_kbd_backlight = true; 1388 else if (err != GB_NOT_SUPPORTED) 1389 return dev_err_probe(&galaxybook->platform->dev, err, 1390 "failed to initialize kbd_backlight\n"); 1391 1392 err = galaxybook_fw_attrs_init(galaxybook); 1393 if (err) 1394 return dev_err_probe(&galaxybook->platform->dev, err, 1395 "failed to initialize firmware-attributes\n"); 1396 1397 err = galaxybook_i8042_filter_install(galaxybook); 1398 if (err) 1399 return dev_err_probe(&galaxybook->platform->dev, err, 1400 "failed to initialize i8042_filter\n"); 1401 1402 return 0; 1403 } 1404 1405 static const struct acpi_device_id galaxybook_device_ids[] = { 1406 { "SAM0426" }, 1407 { "SAM0427" }, 1408 { "SAM0428" }, 1409 { "SAM0429" }, 1410 { "SAM0430" }, 1411 {} 1412 }; 1413 MODULE_DEVICE_TABLE(acpi, galaxybook_device_ids); 1414 1415 static struct platform_driver galaxybook_platform_driver = { 1416 .driver = { 1417 .name = DRIVER_NAME, 1418 .acpi_match_table = galaxybook_device_ids, 1419 }, 1420 .probe = galaxybook_probe, 1421 }; 1422 module_platform_driver(galaxybook_platform_driver); 1423 1424 MODULE_AUTHOR("Joshua Grisham <josh@joshuagrisham.com>"); 1425 MODULE_DESCRIPTION("Samsung Galaxy Book driver"); 1426 MODULE_LICENSE("GPL"); 1427