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