xref: /linux/drivers/platform/x86/samsung-galaxybook.c (revision 4c2cd91bff6371b58e672e8791c3bfa70c1b821f)
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