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