xref: /freebsd/sys/dev/acpi_support/acpi_system76.c (revision f91464171d615b7e7720ac9ed67e2e86392d1b41)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2026 Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include "opt_acpi.h"
30 
31 #include <sys/param.h>
32 #include <sys/bus.h>
33 #include <sys/kernel.h>
34 #include <sys/module.h>
35 #include <sys/sysctl.h>
36 
37 #include <contrib/dev/acpica/include/acpi.h>
38 #include <contrib/dev/acpica/include/accommon.h>
39 
40 #include <dev/acpica/acpivar.h>
41 #include <dev/backlight/backlight.h>
42 
43 #include "backlight_if.h"
44 
45 #define _COMPONENT ACPI_OEM
46 ACPI_MODULE_NAME("system76")
47 
48 static char	*system76_ids[] = { "17761776", NULL };
49 ACPI_SERIAL_DECL(system76, "System76 ACPI management");
50 
51 struct acpi_ctrl {
52 	int	val;
53 	bool	exists;
54 };
55 
56 struct acpi_system76_softc {
57 	device_t	dev;
58 	ACPI_HANDLE	handle;
59 
60 	struct acpi_ctrl	kbb,	/* S76_CTRL_KBB */
61 				kbc,	/* S76_CTRL_KBC */
62 				bctl,	/* S76_CTRL_BCTL */
63 				bcth;	/* S76_CTRL_BCTH */
64 
65 	struct sysctl_ctx_list	sysctl_ctx;
66 	struct sysctl_oid	*sysctl_tree;
67 	struct cdev	*kbb_bkl;
68 	uint8_t		backlight_level;
69 };
70 
71 static int	acpi_system76_probe(device_t);
72 static int	acpi_system76_attach(device_t);
73 static int	acpi_system76_detach(device_t);
74 static int	acpi_system76_suspend(device_t);
75 static int	acpi_system76_resume(device_t);
76 static int	acpi_system76_shutdown(device_t);
77 static void	acpi_system76_init(struct acpi_system76_softc *);
78 static struct acpi_ctrl *
79 		acpi_system76_ctrl_map(struct acpi_system76_softc *, int);
80 static int	acpi_system76_update(struct acpi_system76_softc *, int, bool);
81 static int	acpi_system76_sysctl_handler(SYSCTL_HANDLER_ARGS);
82 static void	acpi_system76_notify_handler(ACPI_HANDLE, uint32_t, void *);
83 static void	acpi_system76_check(struct acpi_system76_softc *);
84 static int	acpi_system76_backlight_update_status(device_t dev,
85 		    struct backlight_props *props);
86 static int	acpi_system76_backlight_get_status(device_t dev,
87 		    struct backlight_props *props);
88 static int	acpi_system76_backlight_get_info(device_t dev,
89 		    struct backlight_info *info);
90 
91 /* methods */
92 enum {
93 	S76_CTRL_KBB	= 1,	/* Keyboard Brightness */
94 	S76_CTRL_KBC	= 2,	/* Keyboard Color */
95 	S76_CTRL_BCTL	= 3,	/* Battery Charging Start Thresholds */
96 	S76_CTRL_BCTH	= 4,	/* Battery Charging End Thresholds */
97 };
98 #define	S76_CTRL_MAX	5
99 
100 struct s76_ctrl_table {
101 	char	*name;
102 	char	*get_method;
103 #define S76_CTRL_GKBB	"\\_SB.S76D.GKBB"
104 #define S76_CTRL_GKBC	"\\_SB.S76D.GKBC"
105 #define S76_CTRL_GBCT	"\\_SB.PCI0.LPCB.EC0.GBCT"
106 
107 	char	*set_method;
108 #define S76_CTRL_SKBB	"\\_SB.S76D.SKBB"
109 #define S76_CTRL_SKBC	"\\_SB.S76D.SKBC"
110 #define S76_CTRL_SBCT	"\\_SB.PCI0.LPCB.EC0.SBCT"
111 
112 	char	*desc;
113 };
114 
115 static const struct s76_ctrl_table s76_sysctl_table[] = {
116 	[S76_CTRL_KBB] = {
117 		.name = "keyboard_backlight",
118 		.get_method = S76_CTRL_GKBB,
119 		.set_method = S76_CTRL_SKBB,
120 		.desc = "Keyboard Backlight",
121 	},
122 	[S76_CTRL_KBC] = {
123 		.name = "keyboard_color",
124 		.get_method = S76_CTRL_GKBC,
125 		.set_method = S76_CTRL_SKBC,
126 		.desc = "Keyboard Color",
127 	},
128 	[S76_CTRL_BCTL] = {
129 		.name = "battery_charge_min",
130 		.get_method = S76_CTRL_GBCT,
131 		.set_method = S76_CTRL_SBCT,
132 		.desc = "Start charging the battery when this threshold is reached (percentage)",
133 	},
134 	[S76_CTRL_BCTH] = {
135 		.name = "battery_charge_max",
136 		.get_method = S76_CTRL_GBCT,
137 		.set_method = S76_CTRL_SBCT,
138 		.desc = "Stop charging the battery when this threshold is reached (percentage)",
139 	},
140 };
141 
142 static device_method_t acpi_system76_methods[] = {
143 	/* Device interface */
144 	DEVMETHOD(device_probe, acpi_system76_probe),
145 	DEVMETHOD(device_attach, acpi_system76_attach),
146 	DEVMETHOD(device_detach, acpi_system76_detach),
147 	DEVMETHOD(device_suspend, acpi_system76_suspend),
148 	DEVMETHOD(device_resume, acpi_system76_resume),
149 	DEVMETHOD(device_shutdown, acpi_system76_shutdown),
150 
151 	/* Backlight interface */
152         DEVMETHOD(backlight_update_status, acpi_system76_backlight_update_status),
153         DEVMETHOD(backlight_get_status, acpi_system76_backlight_get_status),
154         DEVMETHOD(backlight_get_info, acpi_system76_backlight_get_info),
155 
156 	DEVMETHOD_END
157 };
158 
159 /* Notify event */
160 #define	ACPI_NOTIFY_BACKLIGHT_CHANGED	0x80
161 #define	ACPI_NOTIFY_COLOR_TOGGLE	0x81
162 #define	ACPI_NOTIFY_COLOR_DOWN		0x82
163 #define	ACPI_NOTIFY_COLOR_UP		0x83
164 #define	ACPI_NOTIFY_COLOR_CHANGED	0x84
165 
166 static driver_t acpi_system76_driver = {
167 	"acpi_system76",
168 	acpi_system76_methods,
169 	sizeof(struct acpi_system76_softc)
170 };
171 
172 static const uint32_t acpi_system76_backlight_levels[] = {
173 	0, 6, 12, 18, 24, 30, 36, 42,
174 	48, 54, 60, 66, 72, 78, 84, 100
175 };
176 
177 static inline uint32_t
devstate_to_backlight(uint32_t val)178 devstate_to_backlight(uint32_t val)
179 {
180 	return (acpi_system76_backlight_levels[val >> 4 & 0xf]);
181 }
182 
183 static inline uint32_t
backlight_to_devstate(uint32_t bkl)184 backlight_to_devstate(uint32_t bkl)
185 {
186 	int i;
187 	uint32_t val;
188 
189 	for (i = 0; i < nitems(acpi_system76_backlight_levels); i++) {
190 		if (bkl < acpi_system76_backlight_levels[i])
191 			break;
192 	}
193 	val = (i - 1) * 16;
194 	if (val > 224)
195 		val = 255;
196 	return (val);
197 }
198 
199 /*
200  * Returns corresponding acpi_ctrl of softc from method
201  */
202 static struct acpi_ctrl *
acpi_system76_ctrl_map(struct acpi_system76_softc * sc,int method)203 acpi_system76_ctrl_map(struct acpi_system76_softc *sc, int method)
204 {
205 
206 	switch (method) {
207 	case S76_CTRL_KBB:
208 		return (&sc->kbb);
209 	case S76_CTRL_KBC:
210 		return (&sc->kbc);
211 	case S76_CTRL_BCTL:
212 		return (&sc->bctl);
213 	case S76_CTRL_BCTH:
214 		return (&sc->bcth);
215 	default:
216 		device_printf(sc->dev, "Driver received unknown method\n");
217 		return (NULL);
218 	}
219 }
220 
221 static int
acpi_system76_update(struct acpi_system76_softc * sc,int method,bool set)222 acpi_system76_update(struct acpi_system76_softc *sc, int method, bool set)
223 {
224 	struct acpi_ctrl *ctrl;
225 	ACPI_STATUS status;
226 	ACPI_BUFFER Buf;
227 	ACPI_OBJECT Arg[2], Obj;
228 	ACPI_OBJECT_LIST Args;
229 
230 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
231 	ACPI_SERIAL_ASSERT(system76);
232 
233 	if ((ctrl = acpi_system76_ctrl_map(sc, method)) == NULL)
234 		return (EINVAL);
235 
236 	switch (method) {
237 	case S76_CTRL_BCTL:
238 	case S76_CTRL_BCTH:
239 		Arg[0].Type = ACPI_TYPE_INTEGER;
240 		Arg[0].Integer.Value = method == S76_CTRL_BCTH ? 1 : 0;
241 		Args.Count = set ? 2 : 1;
242 		Args.Pointer = Arg;
243 		Buf.Length = sizeof(Obj);
244 		Buf.Pointer = &Obj;
245 
246 		if (set) {
247 			Arg[1].Type = ACPI_TYPE_INTEGER;
248 			Arg[1].Integer.Value = ctrl->val;
249 
250 			status = AcpiEvaluateObject(sc->handle,
251 			    s76_sysctl_table[method].set_method, &Args, &Buf);
252 		} else {
253 			status = AcpiEvaluateObject(sc->handle,
254 			    s76_sysctl_table[method].get_method, &Args, &Buf);
255 			if (ACPI_SUCCESS(status) &&
256 			    Obj.Type == ACPI_TYPE_INTEGER)
257 				ctrl->val = Obj.Integer.Value;
258 		}
259 		break;
260 	case S76_CTRL_KBB:
261 	case S76_CTRL_KBC:
262 		if (set)
263 			status = acpi_SetInteger(sc->handle, s76_sysctl_table[method].set_method,
264 			    ctrl->val);
265 		else
266 			status = acpi_GetInteger(sc->handle, s76_sysctl_table[method].get_method,
267 			    &ctrl->val);
268 		break;
269 	}
270 
271 	if (ACPI_FAILURE(status)) {
272 		device_printf(sc->dev, "Couldn't query method (%s)\n",
273 		    s76_sysctl_table[method].name);
274 		return (status);
275 	}
276 
277 	return (0);
278 }
279 
280 static void
acpi_system76_notify_update(void * arg)281 acpi_system76_notify_update(void *arg)
282 {
283 	struct acpi_system76_softc *sc;
284 	int method;
285 
286 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
287 
288 	sc = (struct acpi_system76_softc *)device_get_softc(arg);
289 
290 	ACPI_SERIAL_BEGIN(system76);
291 	for (method = 1; method < S76_CTRL_MAX; method++) {
292 		if (method == S76_CTRL_BCTL ||
293 		    method == S76_CTRL_BCTH)
294 			continue;
295 		acpi_system76_update(sc, method, false);
296 	}
297 	ACPI_SERIAL_END(system76);
298 
299 	if (sc->kbb_bkl != NULL)
300 		sc->backlight_level = devstate_to_backlight(sc->kbb.val);
301 }
302 
303 static void
acpi_system76_check(struct acpi_system76_softc * sc)304 acpi_system76_check(struct acpi_system76_softc *sc)
305 {
306 	struct acpi_ctrl *ctrl;
307 	int method;
308 
309 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
310 	ACPI_SERIAL_ASSERT(system76);
311 
312 	for (method = 1; method < S76_CTRL_MAX; method++) {
313 		if ((ctrl = acpi_system76_ctrl_map(sc, method)) == NULL)
314 			continue;
315 
316 		/* available in all models */
317 		if (method == S76_CTRL_BCTL ||
318 		    method == S76_CTRL_BCTH) {
319 			ctrl->exists = true;
320 			acpi_system76_update(sc, method, false);
321 			continue;
322 		}
323 
324 		if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
325 		    s76_sysctl_table[method].get_method, &ctrl->val))) {
326 			ctrl->exists = false;
327 			device_printf(sc->dev, "Driver can't control %s\n",
328 			    s76_sysctl_table[method].desc);
329 		} else {
330 			ctrl->exists = true;
331 			acpi_system76_update(sc, method, false);
332 		}
333 	}
334 }
335 
336 static void
acpi_system76_notify_handler(ACPI_HANDLE handle,uint32_t notify,void * ctx)337 acpi_system76_notify_handler(ACPI_HANDLE handle, uint32_t notify, void *ctx)
338 {
339 
340 	ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
341 
342 	switch (notify) {
343 	case ACPI_NOTIFY_BACKLIGHT_CHANGED:
344 	case ACPI_NOTIFY_COLOR_TOGGLE:
345 	case ACPI_NOTIFY_COLOR_DOWN:
346 	case ACPI_NOTIFY_COLOR_UP:
347 	case ACPI_NOTIFY_COLOR_CHANGED:
348 		AcpiOsExecute(OSL_NOTIFY_HANDLER,
349 		    acpi_system76_notify_update, ctx);
350 		break;
351 	default:
352 		break;
353 	}
354 }
355 
356 static int
acpi_system76_sysctl_handler(SYSCTL_HANDLER_ARGS)357 acpi_system76_sysctl_handler(SYSCTL_HANDLER_ARGS)
358 {
359 	struct acpi_ctrl *ctrl, *ctrl_cmp;
360 	struct acpi_system76_softc *sc;
361 	int val, method, error;
362 	bool update;
363 
364 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
365 
366 	sc = (struct acpi_system76_softc *)oidp->oid_arg1;
367 	method = oidp->oid_arg2;
368 	if ((ctrl = acpi_system76_ctrl_map(sc, method)) == NULL)
369 		return (EINVAL);
370 
371 	val = ctrl->val;
372 	error = sysctl_handle_int(oidp, &val, 0, req);
373 	if (error != 0) {
374 		device_printf(sc->dev, "Driver query failed\n");
375 		return (error);
376 	}
377 
378 	if (req->newptr == NULL) {
379 		/*
380 		 * ACPI will not notify us if battery thresholds changes
381 		 * outside this module. Therefore, always fetch those values.
382 		 */
383 		if (method != S76_CTRL_BCTL && method != S76_CTRL_BCTH)
384 			return (error);
385 		update = false;
386 	} else {
387 		/* Input validation */
388 		switch (method) {
389 		case S76_CTRL_KBB:
390 			if (val > UINT8_MAX || val < 0)
391 				return (EINVAL);
392 			if (sc->kbb_bkl != NULL)
393 				sc->backlight_level = devstate_to_backlight(val);
394 			break;
395 		case S76_CTRL_KBC:
396 			if (val >= (1 << 24) || val < 0)
397 				return (EINVAL);
398 			break;
399 		case S76_CTRL_BCTL:
400 			if ((ctrl_cmp = acpi_system76_ctrl_map(sc, S76_CTRL_BCTH)) == NULL)
401 				return (EINVAL);
402 			if (val > 100 || val < 0 || val >= ctrl_cmp->val)
403 				return (EINVAL);
404 			break;
405 		case S76_CTRL_BCTH:
406 			if ((ctrl_cmp = acpi_system76_ctrl_map(sc, S76_CTRL_BCTL)) == NULL)
407 				return (EINVAL);
408 			if (val > 100 || val < 0 || val <= ctrl_cmp->val)
409 				return (EINVAL);
410 			break;
411 		}
412 		ctrl->val = val;
413 		update = true;
414 	}
415 
416 	ACPI_SERIAL_BEGIN(system76);
417 	error = acpi_system76_update(sc, method, update);
418 	ACPI_SERIAL_END(system76);
419 	return (error);
420 }
421 
422 static void
acpi_system76_init(struct acpi_system76_softc * sc)423 acpi_system76_init(struct acpi_system76_softc *sc)
424 {
425 	struct acpi_softc *acpi_sc;
426 	struct acpi_ctrl *ctrl;
427 	uint32_t method;
428 
429 	ACPI_SERIAL_ASSERT(system76);
430 
431 	acpi_system76_check(sc);
432 	acpi_sc = acpi_device_get_parent_softc(sc->dev);
433 	sysctl_ctx_init(&sc->sysctl_ctx);
434 	sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
435 	    SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO, "s76",
436 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "system76 control");
437 
438 	for (method = 1; method < S76_CTRL_MAX; method++) {
439 		if ((ctrl = acpi_system76_ctrl_map(sc, method)) == NULL)
440 			continue;
441 
442 		if (!ctrl->exists)
443 			continue;
444 
445 		if (method == S76_CTRL_KBB) {
446 			sc->kbb_bkl = backlight_register("system76_keyboard", sc->dev);
447 			if (sc->kbb_bkl == NULL)
448 				device_printf(sc->dev, "Can not register backlight\n");
449 			else
450 				sc->backlight_level = devstate_to_backlight(sc->kbb.val);
451 		}
452 
453 		SYSCTL_ADD_PROC(&sc->sysctl_ctx,
454 		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, s76_sysctl_table[method].name,
455 		    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MPSAFE,
456 		    sc, method, acpi_system76_sysctl_handler, "IU", s76_sysctl_table[method].desc);
457 	}
458 }
459 
460 static int
acpi_system76_backlight_update_status(device_t dev,struct backlight_props * props)461 acpi_system76_backlight_update_status(device_t dev, struct backlight_props
462     *props)
463 {
464 	struct acpi_system76_softc *sc;
465 
466 	sc = device_get_softc(dev);
467 	if (sc->kbb.val != backlight_to_devstate(props->brightness)) {
468 		sc->kbb.val = backlight_to_devstate(props->brightness);
469 		acpi_system76_update(sc, S76_CTRL_KBB, true);
470 	}
471 	sc->backlight_level = props->brightness;
472 
473 	return (0);
474 }
475 
476 static int
acpi_system76_backlight_get_status(device_t dev,struct backlight_props * props)477 acpi_system76_backlight_get_status(device_t dev, struct backlight_props *props)
478 {
479 	struct acpi_system76_softc *sc;
480 
481 	sc = device_get_softc(dev);
482 	props->brightness = sc->backlight_level;
483 	props->nlevels = nitems(acpi_system76_backlight_levels);
484 	memcpy(props->levels, acpi_system76_backlight_levels,
485 	    sizeof(acpi_system76_backlight_levels));
486 
487         return (0);
488 }
489 
490 static int
acpi_system76_backlight_get_info(device_t dev,struct backlight_info * info)491 acpi_system76_backlight_get_info(device_t dev, struct backlight_info *info)
492 {
493         info->type = BACKLIGHT_TYPE_KEYBOARD;
494         strlcpy(info->name, "System76 Keyboard", BACKLIGHTMAXNAMELENGTH);
495 
496         return (0);
497 }
498 
499 static int
acpi_system76_attach(device_t dev)500 acpi_system76_attach(device_t dev)
501 {
502 	struct acpi_system76_softc *sc;
503 
504 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
505 
506 	sc = device_get_softc(dev);
507 	sc->dev = dev;
508 	sc->handle = acpi_get_handle(dev);
509 
510 	AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
511 	    acpi_system76_notify_handler, dev);
512 
513 	ACPI_SERIAL_BEGIN(system76);
514 	acpi_system76_init(sc);
515 	ACPI_SERIAL_END(system76);
516 
517 	return (0);
518 }
519 
520 static int
acpi_system76_detach(device_t dev)521 acpi_system76_detach(device_t dev)
522 {
523 	struct acpi_system76_softc *sc;
524 
525 	sc = device_get_softc(dev);
526 	if (sysctl_ctx_free(&sc->sysctl_ctx) != 0)
527 		return (EBUSY);
528 
529 	AcpiRemoveNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
530 	    acpi_system76_notify_handler);
531 
532 	if (sc->kbb_bkl != NULL)
533 		backlight_destroy(sc->kbb_bkl);
534 
535 	return (0);
536 }
537 
538 static int
acpi_system76_suspend(device_t dev)539 acpi_system76_suspend(device_t dev)
540 {
541 	struct acpi_system76_softc *sc;
542 	struct acpi_ctrl *ctrl;
543 
544 	sc = device_get_softc(dev);
545 	if ((ctrl = acpi_system76_ctrl_map(sc, S76_CTRL_KBB)) != NULL) {
546 		ctrl->val = 0;
547 		acpi_system76_update(sc, S76_CTRL_KBB, true);
548 	}
549 
550 	return (0);
551 }
552 
553 static int
acpi_system76_resume(device_t dev)554 acpi_system76_resume(device_t dev)
555 {
556 	struct acpi_system76_softc *sc;
557 	struct acpi_ctrl *ctrl;
558 
559 	sc = device_get_softc(dev);
560 	if ((ctrl = acpi_system76_ctrl_map(sc, S76_CTRL_KBB)) != NULL) {
561 		ctrl->val = backlight_to_devstate(sc->backlight_level);
562 		acpi_system76_update(sc, S76_CTRL_KBB, true);
563 	}
564 
565 	return (0);
566 }
567 
568 static int
acpi_system76_shutdown(device_t dev)569 acpi_system76_shutdown(device_t dev)
570 {
571 	return (acpi_system76_detach(dev));
572 }
573 
574 static int
acpi_system76_probe(device_t dev)575 acpi_system76_probe(device_t dev)
576 {
577 	int rv;
578 
579 	if (acpi_disabled("system76") || device_get_unit(dev) > 1)
580 		return (ENXIO);
581 	rv = ACPI_ID_PROBE(device_get_parent(dev), dev, system76_ids, NULL);
582 	if (rv > 0) {
583 		return (rv);
584 	}
585 
586 	return (BUS_PROBE_VENDOR);
587 }
588 
589 DRIVER_MODULE(acpi_system76, acpi, acpi_system76_driver, 0, 0);
590 MODULE_VERSION(acpi_system76, 1);
591 MODULE_DEPEND(acpi_system76, acpi, 1, 1, 1);
592 MODULE_DEPEND(acpi_system76, backlight, 1, 1, 1);
593