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
galaxybook_acpi_method(struct samsung_galaxybook * galaxybook,acpi_string method,struct sawb * buf,size_t len)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
galaxybook_enable_acpi_feature(struct samsung_galaxybook * galaxybook,const u16 sasb)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
kbd_backlight_acpi_get(struct samsung_galaxybook * galaxybook,enum led_brightness * brightness)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
kbd_backlight_acpi_set(struct samsung_galaxybook * galaxybook,const enum led_brightness brightness)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
kbd_backlight_show(struct led_classdev * led)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
kbd_backlight_store(struct led_classdev * led,const enum led_brightness brightness)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
galaxybook_kbd_backlight_init(struct samsung_galaxybook * galaxybook)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
charge_control_end_threshold_acpi_get(struct samsung_galaxybook * galaxybook,u8 * value)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
charge_control_end_threshold_acpi_set(struct samsung_galaxybook * galaxybook,u8 value)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
galaxybook_battery_ext_property_get(struct power_supply * psy,const struct power_supply_ext * ext,void * ext_data,enum power_supply_property psp,union power_supply_propval * val)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
galaxybook_battery_ext_property_set(struct power_supply * psy,const struct power_supply_ext * ext,void * ext_data,enum power_supply_property psp,const union power_supply_propval * val)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
galaxybook_battery_ext_property_is_writeable(struct power_supply * psy,const struct power_supply_ext * ext,void * ext_data,enum power_supply_property psp)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
galaxybook_battery_add(struct power_supply * battery,struct acpi_battery_hook * hook)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
galaxybook_battery_remove(struct power_supply * battery,struct acpi_battery_hook * hook)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
galaxybook_battery_threshold_init(struct samsung_galaxybook * galaxybook)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
performance_mode_acpi_get(struct samsung_galaxybook * galaxybook,u8 * performance_mode)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
performance_mode_acpi_set(struct samsung_galaxybook * galaxybook,const u8 performance_mode)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
get_performance_mode_profile(struct samsung_galaxybook * galaxybook,const u8 performance_mode,enum platform_profile_option * profile)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
galaxybook_platform_profile_get(struct device * dev,enum platform_profile_option * profile)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
galaxybook_platform_profile_set(struct device * dev,enum platform_profile_option profile)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
galaxybook_platform_profile_probe(void * drvdata,unsigned long * choices)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
galaxybook_platform_profile_init(struct samsung_galaxybook * galaxybook)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
power_on_lid_open_acpi_get(struct samsung_galaxybook * galaxybook,bool * value)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
power_on_lid_open_acpi_set(struct samsung_galaxybook * galaxybook,const bool value)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
usb_charging_acpi_get(struct samsung_galaxybook * galaxybook,bool * value)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
usb_charging_acpi_set(struct samsung_galaxybook * galaxybook,const bool value)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
block_recording_acpi_get(struct samsung_galaxybook * galaxybook,bool * value)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
block_recording_acpi_set(struct samsung_galaxybook * galaxybook,const bool value)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
galaxybook_block_recording_init(struct samsung_galaxybook * galaxybook)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
type_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)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
default_value_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)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
possible_values_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)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
display_name_language_code_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)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
display_name_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)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
current_value_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)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
current_value_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)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
galaxybook_fw_attr_init(struct samsung_galaxybook * galaxybook,const enum galaxybook_fw_attr_id fw_attr_id,int (* get_value)(struct samsung_galaxybook * galaxybook,bool * value),int (* set_value)(struct samsung_galaxybook * galaxybook,const bool value))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
galaxybook_kset_unregister(void * data)1039 static void galaxybook_kset_unregister(void *data)
1040 {
1041 struct kset *kset = data;
1042
1043 kset_unregister(kset);
1044 }
1045
galaxybook_fw_attrs_dev_unregister(void * data)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
galaxybook_fw_attrs_init(struct samsung_galaxybook * galaxybook)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
galaxybook_kbd_backlight_hotkey_work(struct work_struct * work)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
galaxybook_block_recording_hotkey_work(struct work_struct * work)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
galaxybook_i8042_filter(unsigned char data,unsigned char str,struct serio * port,void * context)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
galaxybook_i8042_filter_remove(void * data)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
galaxybook_i8042_filter_install(struct samsung_galaxybook * galaxybook)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
galaxybook_acpi_notify(acpi_handle handle,u32 event,void * data)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
galaxybook_enable_acpi_notify(struct samsung_galaxybook * galaxybook)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
galaxybook_acpi_remove_notify_handler(void * data)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
galaxybook_acpi_disable(void * data)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
galaxybook_acpi_init(struct samsung_galaxybook * galaxybook)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
galaxybook_probe(struct platform_device * pdev)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