Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/dev/acpi_support/acpi_system76.c
178428 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2026 Pouria Mousavizadeh Tehrani <[email protected]>
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
178
devstate_to_backlight(uint32_t val)
179
{
180
return (acpi_system76_backlight_levels[val >> 4 & 0xf]);
181
}
182
183
static inline uint32_t
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 *
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
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
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
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
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
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
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
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
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
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
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
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
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
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
569
acpi_system76_shutdown(device_t dev)
570
{
571
return (acpi_system76_detach(dev));
572
}
573
574
static int
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
594