Merge tag 'rproc-v5.17' of git://git.kernel.org/pub/scm/linux/kernel/git/remoteproc...
[linux-2.6-microblaze.git] / drivers / platform / x86 / system76_acpi.c
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/power_supply.h>
22 #include <linux/sysfs.h>
23 #include <linux/types.h>
24
25 #include <acpi/battery.h>
26
27 struct system76_data {
28         struct acpi_device *acpi_dev;
29         struct led_classdev ap_led;
30         struct led_classdev kb_led;
31         enum led_brightness kb_brightness;
32         enum led_brightness kb_toggle_brightness;
33         int kb_color;
34         struct device *therm;
35         union acpi_object *nfan;
36         union acpi_object *ntmp;
37         struct input_dev *input;
38         bool has_open_ec;
39 };
40
41 static const struct acpi_device_id device_ids[] = {
42         {"17761776", 0},
43         {"", 0},
44 };
45 MODULE_DEVICE_TABLE(acpi, device_ids);
46
47 // Array of keyboard LED brightness levels
48 static const enum led_brightness kb_levels[] = {
49         48,
50         72,
51         96,
52         144,
53         192,
54         255
55 };
56
57 // Array of keyboard LED colors in 24-bit RGB format
58 static const int kb_colors[] = {
59         0xFFFFFF,
60         0x0000FF,
61         0xFF0000,
62         0xFF00FF,
63         0x00FF00,
64         0x00FFFF,
65         0xFFFF00
66 };
67
68 // Get a System76 ACPI device value by name
69 static int system76_get(struct system76_data *data, char *method)
70 {
71         acpi_handle handle;
72         acpi_status status;
73         unsigned long long ret = 0;
74
75         handle = acpi_device_handle(data->acpi_dev);
76         status = acpi_evaluate_integer(handle, method, NULL, &ret);
77         if (ACPI_SUCCESS(status))
78                 return ret;
79         return -ENODEV;
80 }
81
82 // Get a System76 ACPI device value by name with index
83 static int system76_get_index(struct system76_data *data, char *method, int index)
84 {
85         union acpi_object obj;
86         struct acpi_object_list obj_list;
87         acpi_handle handle;
88         acpi_status status;
89         unsigned long long ret = 0;
90
91         obj.type = ACPI_TYPE_INTEGER;
92         obj.integer.value = index;
93         obj_list.count = 1;
94         obj_list.pointer = &obj;
95
96         handle = acpi_device_handle(data->acpi_dev);
97         status = acpi_evaluate_integer(handle, method, &obj_list, &ret);
98         if (ACPI_SUCCESS(status))
99                 return ret;
100         return -ENODEV;
101 }
102
103 // Get a System76 ACPI device object by name
104 static int system76_get_object(struct system76_data *data, char *method, union acpi_object **obj)
105 {
106         acpi_handle handle;
107         acpi_status status;
108         struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
109
110         handle = acpi_device_handle(data->acpi_dev);
111         status = acpi_evaluate_object(handle, method, NULL, &buf);
112         if (ACPI_SUCCESS(status)) {
113                 *obj = buf.pointer;
114                 return 0;
115         }
116
117         return -ENODEV;
118 }
119
120 // Get a name from a System76 ACPI device object
121 static char *system76_name(union acpi_object *obj, int index)
122 {
123         if (obj && obj->type == ACPI_TYPE_PACKAGE && index <= obj->package.count) {
124                 if (obj->package.elements[index].type == ACPI_TYPE_STRING)
125                         return obj->package.elements[index].string.pointer;
126         }
127
128         return NULL;
129 }
130
131 // Set a System76 ACPI device value by name
132 static int system76_set(struct system76_data *data, char *method, int value)
133 {
134         union acpi_object obj;
135         struct acpi_object_list obj_list;
136         acpi_handle handle;
137         acpi_status status;
138
139         obj.type = ACPI_TYPE_INTEGER;
140         obj.integer.value = value;
141         obj_list.count = 1;
142         obj_list.pointer = &obj;
143         handle = acpi_device_handle(data->acpi_dev);
144         status = acpi_evaluate_object(handle, method, &obj_list, NULL);
145         if (ACPI_SUCCESS(status))
146                 return 0;
147         else
148                 return -1;
149 }
150
151 #define BATTERY_THRESHOLD_INVALID       0xFF
152
153 enum {
154         THRESHOLD_START,
155         THRESHOLD_END,
156 };
157
158 static ssize_t battery_get_threshold(int which, char *buf)
159 {
160         struct acpi_object_list input;
161         union acpi_object param;
162         acpi_handle handle;
163         acpi_status status;
164         unsigned long long ret = BATTERY_THRESHOLD_INVALID;
165
166         handle = ec_get_handle();
167         if (!handle)
168                 return -ENODEV;
169
170         input.count = 1;
171         input.pointer = &param;
172         // Start/stop selection
173         param.type = ACPI_TYPE_INTEGER;
174         param.integer.value = which;
175
176         status = acpi_evaluate_integer(handle, "GBCT", &input, &ret);
177         if (ACPI_FAILURE(status))
178                 return -EIO;
179         if (ret == BATTERY_THRESHOLD_INVALID)
180                 return -EINVAL;
181
182         return sysfs_emit(buf, "%d\n", (int)ret);
183 }
184
185 static ssize_t battery_set_threshold(int which, const char *buf, size_t count)
186 {
187         struct acpi_object_list input;
188         union acpi_object params[2];
189         acpi_handle handle;
190         acpi_status status;
191         unsigned int value;
192         int ret;
193
194         handle = ec_get_handle();
195         if (!handle)
196                 return -ENODEV;
197
198         ret = kstrtouint(buf, 10, &value);
199         if (ret)
200                 return ret;
201
202         if (value > 100)
203                 return -EINVAL;
204
205         input.count = 2;
206         input.pointer = params;
207         // Start/stop selection
208         params[0].type = ACPI_TYPE_INTEGER;
209         params[0].integer.value = which;
210         // Threshold value
211         params[1].type = ACPI_TYPE_INTEGER;
212         params[1].integer.value = value;
213
214         status = acpi_evaluate_object(handle, "SBCT", &input, NULL);
215         if (ACPI_FAILURE(status))
216                 return -EIO;
217
218         return count;
219 }
220
221 static ssize_t charge_control_start_threshold_show(struct device *dev,
222         struct device_attribute *attr, char *buf)
223 {
224         return battery_get_threshold(THRESHOLD_START, buf);
225 }
226
227 static ssize_t charge_control_start_threshold_store(struct device *dev,
228         struct device_attribute *attr, const char *buf, size_t count)
229 {
230         return battery_set_threshold(THRESHOLD_START, buf, count);
231 }
232
233 static DEVICE_ATTR_RW(charge_control_start_threshold);
234
235 static ssize_t charge_control_end_threshold_show(struct device *dev,
236         struct device_attribute *attr, char *buf)
237 {
238         return battery_get_threshold(THRESHOLD_END, buf);
239 }
240
241 static ssize_t charge_control_end_threshold_store(struct device *dev,
242         struct device_attribute *attr, const char *buf, size_t count)
243 {
244         return battery_set_threshold(THRESHOLD_END, buf, count);
245 }
246
247 static DEVICE_ATTR_RW(charge_control_end_threshold);
248
249 static struct attribute *system76_battery_attrs[] = {
250         &dev_attr_charge_control_start_threshold.attr,
251         &dev_attr_charge_control_end_threshold.attr,
252         NULL,
253 };
254
255 ATTRIBUTE_GROUPS(system76_battery);
256
257 static int system76_battery_add(struct power_supply *battery)
258 {
259         // System76 EC only supports 1 battery
260         if (strcmp(battery->desc->name, "BAT0") != 0)
261                 return -ENODEV;
262
263         if (device_add_groups(&battery->dev, system76_battery_groups))
264                 return -ENODEV;
265
266         return 0;
267 }
268
269 static int system76_battery_remove(struct power_supply *battery)
270 {
271         device_remove_groups(&battery->dev, system76_battery_groups);
272         return 0;
273 }
274
275 static struct acpi_battery_hook system76_battery_hook = {
276         .add_battery = system76_battery_add,
277         .remove_battery = system76_battery_remove,
278         .name = "System76 Battery Extension",
279 };
280
281 static void system76_battery_init(void)
282 {
283         battery_hook_register(&system76_battery_hook);
284 }
285
286 static void system76_battery_exit(void)
287 {
288         battery_hook_unregister(&system76_battery_hook);
289 }
290
291 // Get the airplane mode LED brightness
292 static enum led_brightness ap_led_get(struct led_classdev *led)
293 {
294         struct system76_data *data;
295         int value;
296
297         data = container_of(led, struct system76_data, ap_led);
298         value = system76_get(data, "GAPL");
299         if (value > 0)
300                 return (enum led_brightness)value;
301         else
302                 return LED_OFF;
303 }
304
305 // Set the airplane mode LED brightness
306 static int ap_led_set(struct led_classdev *led, enum led_brightness value)
307 {
308         struct system76_data *data;
309
310         data = container_of(led, struct system76_data, ap_led);
311         return system76_set(data, "SAPL", value == LED_OFF ? 0 : 1);
312 }
313
314 // Get the last set keyboard LED brightness
315 static enum led_brightness kb_led_get(struct led_classdev *led)
316 {
317         struct system76_data *data;
318
319         data = container_of(led, struct system76_data, kb_led);
320         return data->kb_brightness;
321 }
322
323 // Set the keyboard LED brightness
324 static int kb_led_set(struct led_classdev *led, enum led_brightness value)
325 {
326         struct system76_data *data;
327
328         data = container_of(led, struct system76_data, kb_led);
329         data->kb_brightness = value;
330         return system76_set(data, "SKBL", (int)data->kb_brightness);
331 }
332
333 // Get the last set keyboard LED color
334 static ssize_t kb_led_color_show(
335         struct device *dev,
336         struct device_attribute *dev_attr,
337         char *buf)
338 {
339         struct led_classdev *led;
340         struct system76_data *data;
341
342         led = (struct led_classdev *)dev->driver_data;
343         data = container_of(led, struct system76_data, kb_led);
344         return sysfs_emit(buf, "%06X\n", data->kb_color);
345 }
346
347 // Set the keyboard LED color
348 static ssize_t kb_led_color_store(
349         struct device *dev,
350         struct device_attribute *dev_attr,
351         const char *buf,
352         size_t size)
353 {
354         struct led_classdev *led;
355         struct system76_data *data;
356         unsigned int val;
357         int ret;
358
359         led = (struct led_classdev *)dev->driver_data;
360         data = container_of(led, struct system76_data, kb_led);
361         ret = kstrtouint(buf, 16, &val);
362         if (ret)
363                 return ret;
364         if (val > 0xFFFFFF)
365                 return -EINVAL;
366         data->kb_color = (int)val;
367         system76_set(data, "SKBC", data->kb_color);
368
369         return size;
370 }
371
372 static struct device_attribute dev_attr_kb_led_color = {
373         .attr = {
374                 .name = "color",
375                 .mode = 0644,
376         },
377         .show = kb_led_color_show,
378         .store = kb_led_color_store,
379 };
380
381 static struct attribute *system76_kb_led_color_attrs[] = {
382         &dev_attr_kb_led_color.attr,
383         NULL,
384 };
385
386 ATTRIBUTE_GROUPS(system76_kb_led_color);
387
388 // Notify that the keyboard LED was changed by hardware
389 static void kb_led_notify(struct system76_data *data)
390 {
391         led_classdev_notify_brightness_hw_changed(
392                 &data->kb_led,
393                 data->kb_brightness
394         );
395 }
396
397 // Read keyboard LED brightness as set by hardware
398 static void kb_led_hotkey_hardware(struct system76_data *data)
399 {
400         int value;
401
402         value = system76_get(data, "GKBL");
403         if (value < 0)
404                 return;
405         data->kb_brightness = value;
406         kb_led_notify(data);
407 }
408
409 // Toggle the keyboard LED
410 static void kb_led_hotkey_toggle(struct system76_data *data)
411 {
412         if (data->kb_brightness > 0) {
413                 data->kb_toggle_brightness = data->kb_brightness;
414                 kb_led_set(&data->kb_led, 0);
415         } else {
416                 kb_led_set(&data->kb_led, data->kb_toggle_brightness);
417         }
418         kb_led_notify(data);
419 }
420
421 // Decrease the keyboard LED brightness
422 static void kb_led_hotkey_down(struct system76_data *data)
423 {
424         int i;
425
426         if (data->kb_brightness > 0) {
427                 for (i = ARRAY_SIZE(kb_levels); i > 0; i--) {
428                         if (kb_levels[i - 1] < data->kb_brightness) {
429                                 kb_led_set(&data->kb_led, kb_levels[i - 1]);
430                                 break;
431                         }
432                 }
433         } else {
434                 kb_led_set(&data->kb_led, data->kb_toggle_brightness);
435         }
436         kb_led_notify(data);
437 }
438
439 // Increase the keyboard LED brightness
440 static void kb_led_hotkey_up(struct system76_data *data)
441 {
442         int i;
443
444         if (data->kb_brightness > 0) {
445                 for (i = 0; i < ARRAY_SIZE(kb_levels); i++) {
446                         if (kb_levels[i] > data->kb_brightness) {
447                                 kb_led_set(&data->kb_led, kb_levels[i]);
448                                 break;
449                         }
450                 }
451         } else {
452                 kb_led_set(&data->kb_led, data->kb_toggle_brightness);
453         }
454         kb_led_notify(data);
455 }
456
457 // Cycle the keyboard LED color
458 static void kb_led_hotkey_color(struct system76_data *data)
459 {
460         int i;
461
462         if (data->kb_color < 0)
463                 return;
464         if (data->kb_brightness > 0) {
465                 for (i = 0; i < ARRAY_SIZE(kb_colors); i++) {
466                         if (kb_colors[i] == data->kb_color)
467                                 break;
468                 }
469                 i += 1;
470                 if (i >= ARRAY_SIZE(kb_colors))
471                         i = 0;
472                 data->kb_color = kb_colors[i];
473                 system76_set(data, "SKBC", data->kb_color);
474         } else {
475                 kb_led_set(&data->kb_led, data->kb_toggle_brightness);
476         }
477         kb_led_notify(data);
478 }
479
480 static umode_t thermal_is_visible(const void *drvdata, enum hwmon_sensor_types type,
481                                   u32 attr, int channel)
482 {
483         const struct system76_data *data = drvdata;
484
485         switch (type) {
486         case hwmon_fan:
487         case hwmon_pwm:
488                 if (system76_name(data->nfan, channel))
489                         return 0444;
490                 break;
491
492         case hwmon_temp:
493                 if (system76_name(data->ntmp, channel))
494                         return 0444;
495                 break;
496
497         default:
498                 return 0;
499         }
500
501         return 0;
502 }
503
504 static int thermal_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
505                         int channel, long *val)
506 {
507         struct system76_data *data = dev_get_drvdata(dev);
508         int raw;
509
510         switch (type) {
511         case hwmon_fan:
512                 if (attr == hwmon_fan_input) {
513                         raw = system76_get_index(data, "GFAN", channel);
514                         if (raw < 0)
515                                 return raw;
516                         *val = (raw >> 8) & 0xFFFF;
517                         return 0;
518                 }
519                 break;
520
521         case hwmon_pwm:
522                 if (attr == hwmon_pwm_input) {
523                         raw = system76_get_index(data, "GFAN", channel);
524                         if (raw < 0)
525                                 return raw;
526                         *val = raw & 0xFF;
527                         return 0;
528                 }
529                 break;
530
531         case hwmon_temp:
532                 if (attr == hwmon_temp_input) {
533                         raw = system76_get_index(data, "GTMP", channel);
534                         if (raw < 0)
535                                 return raw;
536                         *val = raw * 1000;
537                         return 0;
538                 }
539                 break;
540
541         default:
542                 return -EOPNOTSUPP;
543         }
544
545         return -EOPNOTSUPP;
546 }
547
548 static int thermal_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
549                                int channel, const char **str)
550 {
551         struct system76_data *data = dev_get_drvdata(dev);
552
553         switch (type) {
554         case hwmon_fan:
555                 if (attr == hwmon_fan_label) {
556                         *str = system76_name(data->nfan, channel);
557                         if (*str)
558                                 return 0;
559                 }
560                 break;
561
562         case hwmon_temp:
563                 if (attr == hwmon_temp_label) {
564                         *str = system76_name(data->ntmp, channel);
565                         if (*str)
566                                 return 0;
567                 }
568                 break;
569
570         default:
571                 return -EOPNOTSUPP;
572         }
573
574         return -EOPNOTSUPP;
575 }
576
577 static const struct hwmon_ops thermal_ops = {
578         .is_visible = thermal_is_visible,
579         .read = thermal_read,
580         .read_string = thermal_read_string,
581 };
582
583 // Allocate up to 8 fans and temperatures
584 static const struct hwmon_channel_info *thermal_channel_info[] = {
585         HWMON_CHANNEL_INFO(fan,
586                 HWMON_F_INPUT | HWMON_F_LABEL,
587                 HWMON_F_INPUT | HWMON_F_LABEL,
588                 HWMON_F_INPUT | HWMON_F_LABEL,
589                 HWMON_F_INPUT | HWMON_F_LABEL,
590                 HWMON_F_INPUT | HWMON_F_LABEL,
591                 HWMON_F_INPUT | HWMON_F_LABEL,
592                 HWMON_F_INPUT | HWMON_F_LABEL,
593                 HWMON_F_INPUT | HWMON_F_LABEL),
594         HWMON_CHANNEL_INFO(pwm,
595                 HWMON_PWM_INPUT,
596                 HWMON_PWM_INPUT,
597                 HWMON_PWM_INPUT,
598                 HWMON_PWM_INPUT,
599                 HWMON_PWM_INPUT,
600                 HWMON_PWM_INPUT,
601                 HWMON_PWM_INPUT,
602                 HWMON_PWM_INPUT),
603         HWMON_CHANNEL_INFO(temp,
604                 HWMON_T_INPUT | HWMON_T_LABEL,
605                 HWMON_T_INPUT | HWMON_T_LABEL,
606                 HWMON_T_INPUT | HWMON_T_LABEL,
607                 HWMON_T_INPUT | HWMON_T_LABEL,
608                 HWMON_T_INPUT | HWMON_T_LABEL,
609                 HWMON_T_INPUT | HWMON_T_LABEL,
610                 HWMON_T_INPUT | HWMON_T_LABEL,
611                 HWMON_T_INPUT | HWMON_T_LABEL),
612         NULL
613 };
614
615 static const struct hwmon_chip_info thermal_chip_info = {
616         .ops = &thermal_ops,
617         .info = thermal_channel_info,
618 };
619
620 static void input_key(struct system76_data *data, unsigned int code)
621 {
622         input_report_key(data->input, code, 1);
623         input_sync(data->input);
624
625         input_report_key(data->input, code, 0);
626         input_sync(data->input);
627 }
628
629 // Handle ACPI notification
630 static void system76_notify(struct acpi_device *acpi_dev, u32 event)
631 {
632         struct system76_data *data;
633
634         data = acpi_driver_data(acpi_dev);
635         switch (event) {
636         case 0x80:
637                 kb_led_hotkey_hardware(data);
638                 break;
639         case 0x81:
640                 kb_led_hotkey_toggle(data);
641                 break;
642         case 0x82:
643                 kb_led_hotkey_down(data);
644                 break;
645         case 0x83:
646                 kb_led_hotkey_up(data);
647                 break;
648         case 0x84:
649                 kb_led_hotkey_color(data);
650                 break;
651         case 0x85:
652                 input_key(data, KEY_SCREENLOCK);
653                 break;
654         }
655 }
656
657 // Add a System76 ACPI device
658 static int system76_add(struct acpi_device *acpi_dev)
659 {
660         struct system76_data *data;
661         int err;
662
663         data = devm_kzalloc(&acpi_dev->dev, sizeof(*data), GFP_KERNEL);
664         if (!data)
665                 return -ENOMEM;
666         acpi_dev->driver_data = data;
667         data->acpi_dev = acpi_dev;
668
669         // Some models do not run open EC firmware. Check for an ACPI method
670         // that only exists on open EC to guard functionality specific to it.
671         data->has_open_ec = acpi_has_method(acpi_device_handle(data->acpi_dev), "NFAN");
672
673         err = system76_get(data, "INIT");
674         if (err)
675                 return err;
676         data->ap_led.name = "system76_acpi::airplane";
677         data->ap_led.flags = LED_CORE_SUSPENDRESUME;
678         data->ap_led.brightness_get = ap_led_get;
679         data->ap_led.brightness_set_blocking = ap_led_set;
680         data->ap_led.max_brightness = 1;
681         data->ap_led.default_trigger = "rfkill-none";
682         err = devm_led_classdev_register(&acpi_dev->dev, &data->ap_led);
683         if (err)
684                 return err;
685
686         data->kb_led.name = "system76_acpi::kbd_backlight";
687         data->kb_led.flags = LED_BRIGHT_HW_CHANGED | LED_CORE_SUSPENDRESUME;
688         data->kb_led.brightness_get = kb_led_get;
689         data->kb_led.brightness_set_blocking = kb_led_set;
690         if (acpi_has_method(acpi_device_handle(data->acpi_dev), "SKBC")) {
691                 data->kb_led.max_brightness = 255;
692                 data->kb_led.groups = system76_kb_led_color_groups;
693                 data->kb_toggle_brightness = 72;
694                 data->kb_color = 0xffffff;
695                 system76_set(data, "SKBC", data->kb_color);
696         } else {
697                 data->kb_led.max_brightness = 5;
698                 data->kb_color = -1;
699         }
700         err = devm_led_classdev_register(&acpi_dev->dev, &data->kb_led);
701         if (err)
702                 return err;
703
704         data->input = devm_input_allocate_device(&acpi_dev->dev);
705         if (!data->input)
706                 return -ENOMEM;
707
708         data->input->name = "System76 ACPI Hotkeys";
709         data->input->phys = "system76_acpi/input0";
710         data->input->id.bustype = BUS_HOST;
711         data->input->dev.parent = &acpi_dev->dev;
712         input_set_capability(data->input, EV_KEY, KEY_SCREENLOCK);
713
714         err = input_register_device(data->input);
715         if (err)
716                 goto error;
717
718         if (data->has_open_ec) {
719                 err = system76_get_object(data, "NFAN", &data->nfan);
720                 if (err)
721                         goto error;
722
723                 err = system76_get_object(data, "NTMP", &data->ntmp);
724                 if (err)
725                         goto error;
726
727                 data->therm = devm_hwmon_device_register_with_info(&acpi_dev->dev,
728                         "system76_acpi", data, &thermal_chip_info, NULL);
729                 err = PTR_ERR_OR_ZERO(data->therm);
730                 if (err)
731                         goto error;
732
733                 system76_battery_init();
734         }
735
736         return 0;
737
738 error:
739         if (data->has_open_ec) {
740                 kfree(data->ntmp);
741                 kfree(data->nfan);
742         }
743         return err;
744 }
745
746 // Remove a System76 ACPI device
747 static int system76_remove(struct acpi_device *acpi_dev)
748 {
749         struct system76_data *data;
750
751         data = acpi_driver_data(acpi_dev);
752
753         if (data->has_open_ec) {
754                 system76_battery_exit();
755                 kfree(data->nfan);
756                 kfree(data->ntmp);
757         }
758
759         devm_led_classdev_unregister(&acpi_dev->dev, &data->ap_led);
760         devm_led_classdev_unregister(&acpi_dev->dev, &data->kb_led);
761
762         system76_get(data, "FINI");
763
764         return 0;
765 }
766
767 static struct acpi_driver system76_driver = {
768         .name = "System76 ACPI Driver",
769         .class = "hotkey",
770         .ids = device_ids,
771         .ops = {
772                 .add = system76_add,
773                 .remove = system76_remove,
774                 .notify = system76_notify,
775         },
776 };
777 module_acpi_driver(system76_driver);
778
779 MODULE_DESCRIPTION("System76 ACPI Driver");
780 MODULE_AUTHOR("Jeremy Soller <jeremy@system76.com>");
781 MODULE_LICENSE("GPL");