1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * System76 ACPI Driver
4  *
5  * Copyright (C) 2019 System76
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  */
11 
12 #include <linux/acpi.h>
13 #include <linux/hwmon.h>
14 #include <linux/hwmon-sysfs.h>
15 #include <linux/init.h>
16 #include <linux/input.h>
17 #include <linux/kernel.h>
18 #include <linux/leds.h>
19 #include <linux/module.h>
20 #include <linux/pci_ids.h>
21 #include <linux/types.h>
22 
23 struct system76_data {
24 	struct acpi_device *acpi_dev;
25 	struct led_classdev ap_led;
26 	struct led_classdev kb_led;
27 	enum led_brightness kb_brightness;
28 	enum led_brightness kb_toggle_brightness;
29 	int kb_color;
30 	struct device *therm;
31 	union acpi_object *nfan;
32 	union acpi_object *ntmp;
33 	struct input_dev *input;
34 };
35 
36 static const struct acpi_device_id device_ids[] = {
37 	{"17761776", 0},
38 	{"", 0},
39 };
40 MODULE_DEVICE_TABLE(acpi, device_ids);
41 
42 // Array of keyboard LED brightness levels
43 static const enum led_brightness kb_levels[] = {
44 	48,
45 	72,
46 	96,
47 	144,
48 	192,
49 	255
50 };
51 
52 // Array of keyboard LED colors in 24-bit RGB format
53 static const int kb_colors[] = {
54 	0xFFFFFF,
55 	0x0000FF,
56 	0xFF0000,
57 	0xFF00FF,
58 	0x00FF00,
59 	0x00FFFF,
60 	0xFFFF00
61 };
62 
63 // Get a System76 ACPI device value by name
64 static int system76_get(struct system76_data *data, char *method)
65 {
66 	acpi_handle handle;
67 	acpi_status status;
68 	unsigned long long ret = 0;
69 
70 	handle = acpi_device_handle(data->acpi_dev);
71 	status = acpi_evaluate_integer(handle, method, NULL, &ret);
72 	if (ACPI_SUCCESS(status))
73 		return ret;
74 	return -ENODEV;
75 }
76 
77 // Get a System76 ACPI device value by name with index
78 static int system76_get_index(struct system76_data *data, char *method, int index)
79 {
80 	union acpi_object obj;
81 	struct acpi_object_list obj_list;
82 	acpi_handle handle;
83 	acpi_status status;
84 	unsigned long long ret = 0;
85 
86 	obj.type = ACPI_TYPE_INTEGER;
87 	obj.integer.value = index;
88 	obj_list.count = 1;
89 	obj_list.pointer = &obj;
90 
91 	handle = acpi_device_handle(data->acpi_dev);
92 	status = acpi_evaluate_integer(handle, method, &obj_list, &ret);
93 	if (ACPI_SUCCESS(status))
94 		return ret;
95 	return -ENODEV;
96 }
97 
98 // Get a System76 ACPI device object by name
99 static int system76_get_object(struct system76_data *data, char *method, union acpi_object **obj)
100 {
101 	acpi_handle handle;
102 	acpi_status status;
103 	struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
104 
105 	handle = acpi_device_handle(data->acpi_dev);
106 	status = acpi_evaluate_object(handle, method, NULL, &buf);
107 	if (ACPI_SUCCESS(status)) {
108 		*obj = buf.pointer;
109 		return 0;
110 	}
111 
112 	return -ENODEV;
113 }
114 
115 // Get a name from a System76 ACPI device object
116 static char *system76_name(union acpi_object *obj, int index)
117 {
118 	if (obj && obj->type == ACPI_TYPE_PACKAGE && index <= obj->package.count) {
119 		if (obj->package.elements[index].type == ACPI_TYPE_STRING)
120 			return obj->package.elements[index].string.pointer;
121 	}
122 
123 	return NULL;
124 }
125 
126 // Set a System76 ACPI device value by name
127 static int system76_set(struct system76_data *data, char *method, int value)
128 {
129 	union acpi_object obj;
130 	struct acpi_object_list obj_list;
131 	acpi_handle handle;
132 	acpi_status status;
133 
134 	obj.type = ACPI_TYPE_INTEGER;
135 	obj.integer.value = value;
136 	obj_list.count = 1;
137 	obj_list.pointer = &obj;
138 	handle = acpi_device_handle(data->acpi_dev);
139 	status = acpi_evaluate_object(handle, method, &obj_list, NULL);
140 	if (ACPI_SUCCESS(status))
141 		return 0;
142 	else
143 		return -1;
144 }
145 
146 // Get the airplane mode LED brightness
147 static enum led_brightness ap_led_get(struct led_classdev *led)
148 {
149 	struct system76_data *data;
150 	int value;
151 
152 	data = container_of(led, struct system76_data, ap_led);
153 	value = system76_get(data, "GAPL");
154 	if (value > 0)
155 		return (enum led_brightness)value;
156 	else
157 		return LED_OFF;
158 }
159 
160 // Set the airplane mode LED brightness
161 static int ap_led_set(struct led_classdev *led, enum led_brightness value)
162 {
163 	struct system76_data *data;
164 
165 	data = container_of(led, struct system76_data, ap_led);
166 	return system76_set(data, "SAPL", value == LED_OFF ? 0 : 1);
167 }
168 
169 // Get the last set keyboard LED brightness
170 static enum led_brightness kb_led_get(struct led_classdev *led)
171 {
172 	struct system76_data *data;
173 
174 	data = container_of(led, struct system76_data, kb_led);
175 	return data->kb_brightness;
176 }
177 
178 // Set the keyboard LED brightness
179 static int kb_led_set(struct led_classdev *led, enum led_brightness value)
180 {
181 	struct system76_data *data;
182 
183 	data = container_of(led, struct system76_data, kb_led);
184 	data->kb_brightness = value;
185 	return system76_set(data, "SKBL", (int)data->kb_brightness);
186 }
187 
188 // Get the last set keyboard LED color
189 static ssize_t kb_led_color_show(
190 	struct device *dev,
191 	struct device_attribute *dev_attr,
192 	char *buf)
193 {
194 	struct led_classdev *led;
195 	struct system76_data *data;
196 
197 	led = (struct led_classdev *)dev->driver_data;
198 	data = container_of(led, struct system76_data, kb_led);
199 	return sprintf(buf, "%06X\n", data->kb_color);
200 }
201 
202 // Set the keyboard LED color
203 static ssize_t kb_led_color_store(
204 	struct device *dev,
205 	struct device_attribute *dev_attr,
206 	const char *buf,
207 	size_t size)
208 {
209 	struct led_classdev *led;
210 	struct system76_data *data;
211 	unsigned int val;
212 	int ret;
213 
214 	led = (struct led_classdev *)dev->driver_data;
215 	data = container_of(led, struct system76_data, kb_led);
216 	ret = kstrtouint(buf, 16, &val);
217 	if (ret)
218 		return ret;
219 	if (val > 0xFFFFFF)
220 		return -EINVAL;
221 	data->kb_color = (int)val;
222 	system76_set(data, "SKBC", data->kb_color);
223 
224 	return size;
225 }
226 
227 static const struct device_attribute kb_led_color_dev_attr = {
228 	.attr = {
229 		.name = "color",
230 		.mode = 0644,
231 	},
232 	.show = kb_led_color_show,
233 	.store = kb_led_color_store,
234 };
235 
236 // Notify that the keyboard LED was changed by hardware
237 static void kb_led_notify(struct system76_data *data)
238 {
239 	led_classdev_notify_brightness_hw_changed(
240 		&data->kb_led,
241 		data->kb_brightness
242 	);
243 }
244 
245 // Read keyboard LED brightness as set by hardware
246 static void kb_led_hotkey_hardware(struct system76_data *data)
247 {
248 	int value;
249 
250 	value = system76_get(data, "GKBL");
251 	if (value < 0)
252 		return;
253 	data->kb_brightness = value;
254 	kb_led_notify(data);
255 }
256 
257 // Toggle the keyboard LED
258 static void kb_led_hotkey_toggle(struct system76_data *data)
259 {
260 	if (data->kb_brightness > 0) {
261 		data->kb_toggle_brightness = data->kb_brightness;
262 		kb_led_set(&data->kb_led, 0);
263 	} else {
264 		kb_led_set(&data->kb_led, data->kb_toggle_brightness);
265 	}
266 	kb_led_notify(data);
267 }
268 
269 // Decrease the keyboard LED brightness
270 static void kb_led_hotkey_down(struct system76_data *data)
271 {
272 	int i;
273 
274 	if (data->kb_brightness > 0) {
275 		for (i = ARRAY_SIZE(kb_levels); i > 0; i--) {
276 			if (kb_levels[i - 1] < data->kb_brightness) {
277 				kb_led_set(&data->kb_led, kb_levels[i - 1]);
278 				break;
279 			}
280 		}
281 	} else {
282 		kb_led_set(&data->kb_led, data->kb_toggle_brightness);
283 	}
284 	kb_led_notify(data);
285 }
286 
287 // Increase the keyboard LED brightness
288 static void kb_led_hotkey_up(struct system76_data *data)
289 {
290 	int i;
291 
292 	if (data->kb_brightness > 0) {
293 		for (i = 0; i < ARRAY_SIZE(kb_levels); i++) {
294 			if (kb_levels[i] > data->kb_brightness) {
295 				kb_led_set(&data->kb_led, kb_levels[i]);
296 				break;
297 			}
298 		}
299 	} else {
300 		kb_led_set(&data->kb_led, data->kb_toggle_brightness);
301 	}
302 	kb_led_notify(data);
303 }
304 
305 // Cycle the keyboard LED color
306 static void kb_led_hotkey_color(struct system76_data *data)
307 {
308 	int i;
309 
310 	if (data->kb_color < 0)
311 		return;
312 	if (data->kb_brightness > 0) {
313 		for (i = 0; i < ARRAY_SIZE(kb_colors); i++) {
314 			if (kb_colors[i] == data->kb_color)
315 				break;
316 		}
317 		i += 1;
318 		if (i >= ARRAY_SIZE(kb_colors))
319 			i = 0;
320 		data->kb_color = kb_colors[i];
321 		system76_set(data, "SKBC", data->kb_color);
322 	} else {
323 		kb_led_set(&data->kb_led, data->kb_toggle_brightness);
324 	}
325 	kb_led_notify(data);
326 }
327 
328 static umode_t thermal_is_visible(const void *drvdata, enum hwmon_sensor_types type,
329 				  u32 attr, int channel)
330 {
331 	const struct system76_data *data = drvdata;
332 
333 	switch (type) {
334 	case hwmon_fan:
335 	case hwmon_pwm:
336 		if (system76_name(data->nfan, channel))
337 			return 0444;
338 		break;
339 
340 	case hwmon_temp:
341 		if (system76_name(data->ntmp, channel))
342 			return 0444;
343 		break;
344 
345 	default:
346 		return 0;
347 	}
348 
349 	return 0;
350 }
351 
352 static int thermal_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
353 			int channel, long *val)
354 {
355 	struct system76_data *data = dev_get_drvdata(dev);
356 	int raw;
357 
358 	switch (type) {
359 	case hwmon_fan:
360 		if (attr == hwmon_fan_input) {
361 			raw = system76_get_index(data, "GFAN", channel);
362 			if (raw < 0)
363 				return raw;
364 			*val = (raw >> 8) & 0xFFFF;
365 			return 0;
366 		}
367 		break;
368 
369 	case hwmon_pwm:
370 		if (attr == hwmon_pwm_input) {
371 			raw = system76_get_index(data, "GFAN", channel);
372 			if (raw < 0)
373 				return raw;
374 			*val = raw & 0xFF;
375 			return 0;
376 		}
377 		break;
378 
379 	case hwmon_temp:
380 		if (attr == hwmon_temp_input) {
381 			raw = system76_get_index(data, "GTMP", channel);
382 			if (raw < 0)
383 				return raw;
384 			*val = raw * 1000;
385 			return 0;
386 		}
387 		break;
388 
389 	default:
390 		return -EOPNOTSUPP;
391 	}
392 
393 	return -EOPNOTSUPP;
394 }
395 
396 static int thermal_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
397 			       int channel, const char **str)
398 {
399 	struct system76_data *data = dev_get_drvdata(dev);
400 
401 	switch (type) {
402 	case hwmon_fan:
403 		if (attr == hwmon_fan_label) {
404 			*str = system76_name(data->nfan, channel);
405 			if (*str)
406 				return 0;
407 		}
408 		break;
409 
410 	case hwmon_temp:
411 		if (attr == hwmon_temp_label) {
412 			*str = system76_name(data->ntmp, channel);
413 			if (*str)
414 				return 0;
415 		}
416 		break;
417 
418 	default:
419 		return -EOPNOTSUPP;
420 	}
421 
422 	return -EOPNOTSUPP;
423 }
424 
425 static const struct hwmon_ops thermal_ops = {
426 	.is_visible = thermal_is_visible,
427 	.read = thermal_read,
428 	.read_string = thermal_read_string,
429 };
430 
431 // Allocate up to 8 fans and temperatures
432 static const struct hwmon_channel_info *thermal_channel_info[] = {
433 	HWMON_CHANNEL_INFO(fan,
434 		HWMON_F_INPUT | HWMON_F_LABEL,
435 		HWMON_F_INPUT | HWMON_F_LABEL,
436 		HWMON_F_INPUT | HWMON_F_LABEL,
437 		HWMON_F_INPUT | HWMON_F_LABEL,
438 		HWMON_F_INPUT | HWMON_F_LABEL,
439 		HWMON_F_INPUT | HWMON_F_LABEL,
440 		HWMON_F_INPUT | HWMON_F_LABEL,
441 		HWMON_F_INPUT | HWMON_F_LABEL),
442 	HWMON_CHANNEL_INFO(pwm,
443 		HWMON_PWM_INPUT,
444 		HWMON_PWM_INPUT,
445 		HWMON_PWM_INPUT,
446 		HWMON_PWM_INPUT,
447 		HWMON_PWM_INPUT,
448 		HWMON_PWM_INPUT,
449 		HWMON_PWM_INPUT,
450 		HWMON_PWM_INPUT),
451 	HWMON_CHANNEL_INFO(temp,
452 		HWMON_T_INPUT | HWMON_T_LABEL,
453 		HWMON_T_INPUT | HWMON_T_LABEL,
454 		HWMON_T_INPUT | HWMON_T_LABEL,
455 		HWMON_T_INPUT | HWMON_T_LABEL,
456 		HWMON_T_INPUT | HWMON_T_LABEL,
457 		HWMON_T_INPUT | HWMON_T_LABEL,
458 		HWMON_T_INPUT | HWMON_T_LABEL,
459 		HWMON_T_INPUT | HWMON_T_LABEL),
460 	NULL
461 };
462 
463 static const struct hwmon_chip_info thermal_chip_info = {
464 	.ops = &thermal_ops,
465 	.info = thermal_channel_info,
466 };
467 
468 static void input_key(struct system76_data *data, unsigned int code)
469 {
470 	input_report_key(data->input, code, 1);
471 	input_sync(data->input);
472 
473 	input_report_key(data->input, code, 0);
474 	input_sync(data->input);
475 }
476 
477 // Handle ACPI notification
478 static void system76_notify(struct acpi_device *acpi_dev, u32 event)
479 {
480 	struct system76_data *data;
481 
482 	data = acpi_driver_data(acpi_dev);
483 	switch (event) {
484 	case 0x80:
485 		kb_led_hotkey_hardware(data);
486 		break;
487 	case 0x81:
488 		kb_led_hotkey_toggle(data);
489 		break;
490 	case 0x82:
491 		kb_led_hotkey_down(data);
492 		break;
493 	case 0x83:
494 		kb_led_hotkey_up(data);
495 		break;
496 	case 0x84:
497 		kb_led_hotkey_color(data);
498 		break;
499 	case 0x85:
500 		input_key(data, KEY_SCREENLOCK);
501 		break;
502 	}
503 }
504 
505 // Add a System76 ACPI device
506 static int system76_add(struct acpi_device *acpi_dev)
507 {
508 	struct system76_data *data;
509 	int err;
510 
511 	data = devm_kzalloc(&acpi_dev->dev, sizeof(*data), GFP_KERNEL);
512 	if (!data)
513 		return -ENOMEM;
514 	acpi_dev->driver_data = data;
515 	data->acpi_dev = acpi_dev;
516 
517 	err = system76_get(data, "INIT");
518 	if (err)
519 		return err;
520 	data->ap_led.name = "system76_acpi::airplane";
521 	data->ap_led.flags = LED_CORE_SUSPENDRESUME;
522 	data->ap_led.brightness_get = ap_led_get;
523 	data->ap_led.brightness_set_blocking = ap_led_set;
524 	data->ap_led.max_brightness = 1;
525 	data->ap_led.default_trigger = "rfkill-none";
526 	err = devm_led_classdev_register(&acpi_dev->dev, &data->ap_led);
527 	if (err)
528 		return err;
529 
530 	data->kb_led.name = "system76_acpi::kbd_backlight";
531 	data->kb_led.flags = LED_BRIGHT_HW_CHANGED | LED_CORE_SUSPENDRESUME;
532 	data->kb_led.brightness_get = kb_led_get;
533 	data->kb_led.brightness_set_blocking = kb_led_set;
534 	if (acpi_has_method(acpi_device_handle(data->acpi_dev), "SKBC")) {
535 		data->kb_led.max_brightness = 255;
536 		data->kb_toggle_brightness = 72;
537 		data->kb_color = 0xffffff;
538 		system76_set(data, "SKBC", data->kb_color);
539 	} else {
540 		data->kb_led.max_brightness = 5;
541 		data->kb_color = -1;
542 	}
543 	err = devm_led_classdev_register(&acpi_dev->dev, &data->kb_led);
544 	if (err)
545 		return err;
546 
547 	if (data->kb_color >= 0) {
548 		err = device_create_file(
549 			data->kb_led.dev,
550 			&kb_led_color_dev_attr
551 		);
552 		if (err)
553 			return err;
554 	}
555 
556 	data->input = devm_input_allocate_device(&acpi_dev->dev);
557 	if (!data->input)
558 		return -ENOMEM;
559 
560 	data->input->name = "System76 ACPI Hotkeys";
561 	data->input->phys = "system76_acpi/input0";
562 	data->input->id.bustype = BUS_HOST;
563 	data->input->dev.parent = &acpi_dev->dev;
564 	input_set_capability(data->input, EV_KEY, KEY_SCREENLOCK);
565 
566 	err = input_register_device(data->input);
567 	if (err)
568 		goto error;
569 
570 	err = system76_get_object(data, "NFAN", &data->nfan);
571 	if (err)
572 		goto error;
573 
574 	err = system76_get_object(data, "NTMP", &data->ntmp);
575 	if (err)
576 		goto error;
577 
578 	data->therm = devm_hwmon_device_register_with_info(&acpi_dev->dev,
579 		"system76_acpi", data, &thermal_chip_info, NULL);
580 	err = PTR_ERR_OR_ZERO(data->therm);
581 	if (err)
582 		goto error;
583 
584 	return 0;
585 
586 error:
587 	kfree(data->ntmp);
588 	kfree(data->nfan);
589 	input_free_device(data->input);
590 	return err;
591 }
592 
593 // Remove a System76 ACPI device
594 static int system76_remove(struct acpi_device *acpi_dev)
595 {
596 	struct system76_data *data;
597 
598 	data = acpi_driver_data(acpi_dev);
599 	if (data->kb_color >= 0)
600 		device_remove_file(data->kb_led.dev, &kb_led_color_dev_attr);
601 
602 	devm_led_classdev_unregister(&acpi_dev->dev, &data->ap_led);
603 	devm_led_classdev_unregister(&acpi_dev->dev, &data->kb_led);
604 
605 	kfree(data->nfan);
606 	kfree(data->ntmp);
607 
608 	system76_get(data, "FINI");
609 
610 	return 0;
611 }
612 
613 static struct acpi_driver system76_driver = {
614 	.name = "System76 ACPI Driver",
615 	.class = "hotkey",
616 	.ids = device_ids,
617 	.ops = {
618 		.add = system76_add,
619 		.remove = system76_remove,
620 		.notify = system76_notify,
621 	},
622 };
623 module_acpi_driver(system76_driver);
624 
625 MODULE_DESCRIPTION("System76 ACPI Driver");
626 MODULE_AUTHOR("Jeremy Soller <jeremy@system76.com>");
627 MODULE_LICENSE("GPL");
628