1*3b9d585eSjsg /* $OpenBSD: acpivout.c,v 1.26 2024/04/13 23:44:11 jsg Exp $ */
276adbdb1Spirofti /*
38855e28cSpirofti * Copyright (c) 2009 Paul Irofti <paul@irofti.net>
476adbdb1Spirofti *
576adbdb1Spirofti * Permission to use, copy, modify, and/or distribute this software for any
676adbdb1Spirofti * purpose with or without fee is hereby granted, provided that the above
776adbdb1Spirofti * copyright notice and this permission notice appear in all copies.
876adbdb1Spirofti *
976adbdb1Spirofti * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1076adbdb1Spirofti * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1176adbdb1Spirofti * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1276adbdb1Spirofti * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1376adbdb1Spirofti * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1476adbdb1Spirofti * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1576adbdb1Spirofti * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1676adbdb1Spirofti */
1776adbdb1Spirofti
1876adbdb1Spirofti #include <sys/param.h>
1976adbdb1Spirofti #include <sys/systm.h>
2076adbdb1Spirofti #include <sys/device.h>
2176adbdb1Spirofti #include <sys/malloc.h>
2276adbdb1Spirofti
2376adbdb1Spirofti #include <machine/bus.h>
2476adbdb1Spirofti
2576adbdb1Spirofti #include <dev/acpi/acpivar.h>
2676adbdb1Spirofti #include <dev/acpi/acpidev.h>
2776adbdb1Spirofti #include <dev/acpi/amltypes.h>
2876adbdb1Spirofti #include <dev/acpi/dsdt.h>
2976adbdb1Spirofti
3076adbdb1Spirofti #include <dev/wscons/wsconsio.h>
31d95a16b0Skettenis #include <dev/wscons/wsdisplayvar.h>
3276adbdb1Spirofti
3376adbdb1Spirofti int acpivout_match(struct device *, void *, void *);
3476adbdb1Spirofti void acpivout_attach(struct device *, struct device *, void *);
3576adbdb1Spirofti int acpivout_notify(struct aml_node *, int, void *);
3676adbdb1Spirofti
3776adbdb1Spirofti #ifdef ACPIVIDEO_DEBUG
3876adbdb1Spirofti #define DPRINTF(x) printf x
3976adbdb1Spirofti #else
4076adbdb1Spirofti #define DPRINTF(x)
4176adbdb1Spirofti #endif
4276adbdb1Spirofti
4376adbdb1Spirofti /* Notifications for Output Devices */
4476adbdb1Spirofti #define NOTIFY_BRIGHTNESS_CYCLE 0x85
4576adbdb1Spirofti #define NOTIFY_BRIGHTNESS_UP 0x86
4676adbdb1Spirofti #define NOTIFY_BRIGHTNESS_DOWN 0x87
4776adbdb1Spirofti #define NOTIFY_BRIGHTNESS_ZERO 0x88
4876adbdb1Spirofti #define NOTIFY_DISPLAY_OFF 0x89
4976adbdb1Spirofti
50f34c4d6bSjcs #define BRIGHTNESS_STEP 5
51f34c4d6bSjcs
5276adbdb1Spirofti struct acpivout_softc {
5376adbdb1Spirofti struct device sc_dev;
5476adbdb1Spirofti
5576adbdb1Spirofti bus_space_tag_t sc_iot;
5676adbdb1Spirofti bus_space_handle_t sc_ioh;
5776adbdb1Spirofti
5876adbdb1Spirofti struct acpi_softc *sc_acpi;
5976adbdb1Spirofti struct aml_node *sc_devnode;
6076adbdb1Spirofti
6176adbdb1Spirofti int *sc_bcl;
6276adbdb1Spirofti size_t sc_bcl_len;
63d3747f62Spatrick
64d3747f62Spatrick int sc_brightness;
6576adbdb1Spirofti };
66d602a597Smiod
6776adbdb1Spirofti int acpivout_get_brightness(struct acpivout_softc *);
68d3747f62Spatrick int acpivout_select_brightness(struct acpivout_softc *, int);
699d125c60Smatthieu int acpivout_find_brightness(struct acpivout_softc *, int);
70d3747f62Spatrick void acpivout_set_brightness(void *, int);
7176adbdb1Spirofti void acpivout_get_bcl(struct acpivout_softc *);
7276adbdb1Spirofti
7376adbdb1Spirofti /* wconsole hook functions */
7476adbdb1Spirofti int acpivout_get_param(struct wsdisplay_param *);
7576adbdb1Spirofti int acpivout_set_param(struct wsdisplay_param *);
7676adbdb1Spirofti
77471aeecfSnaddy const struct cfattach acpivout_ca = {
7876adbdb1Spirofti sizeof(struct acpivout_softc), acpivout_match, acpivout_attach
7976adbdb1Spirofti };
8076adbdb1Spirofti
8176adbdb1Spirofti struct cfdriver acpivout_cd = {
8276adbdb1Spirofti NULL, "acpivout", DV_DULL
8376adbdb1Spirofti };
8476adbdb1Spirofti
8576adbdb1Spirofti int
acpivout_match(struct device * parent,void * match,void * aux)8676adbdb1Spirofti acpivout_match(struct device *parent, void *match, void *aux)
8776adbdb1Spirofti {
88b67bea7dSmartynas struct acpi_attach_args *aaa = aux;
8976adbdb1Spirofti struct cfdata *cf = match;
9076adbdb1Spirofti
91b67bea7dSmartynas if (aaa->aaa_name == NULL ||
92b67bea7dSmartynas strcmp(aaa->aaa_name, cf->cf_driver->cd_name) != 0 ||
93b67bea7dSmartynas aaa->aaa_table != NULL)
9476adbdb1Spirofti return (0);
9576adbdb1Spirofti
9676adbdb1Spirofti return (1);
9776adbdb1Spirofti }
9876adbdb1Spirofti
9976adbdb1Spirofti void
acpivout_attach(struct device * parent,struct device * self,void * aux)10076adbdb1Spirofti acpivout_attach(struct device *parent, struct device *self, void *aux)
10176adbdb1Spirofti {
10276adbdb1Spirofti struct acpivout_softc *sc = (struct acpivout_softc *)self;
103b67bea7dSmartynas struct acpi_attach_args *aaa = aux;
10476adbdb1Spirofti
10576adbdb1Spirofti sc->sc_acpi = ((struct acpivideo_softc *)parent)->sc_acpi;
106b67bea7dSmartynas sc->sc_devnode = aaa->aaa_node;
10776adbdb1Spirofti
10876adbdb1Spirofti printf(": %s\n", sc->sc_devnode->name);
10976adbdb1Spirofti
110b67bea7dSmartynas aml_register_notify(sc->sc_devnode, aaa->aaa_dev,
11176adbdb1Spirofti acpivout_notify, sc, ACPIDEV_NOPOLL);
11276adbdb1Spirofti
113d79a363eSpatrick if (!aml_searchname(sc->sc_devnode, "_BQC") ||
114dd1b5e45Sjsg ws_get_param || ws_set_param ||
115dd1b5e45Sjsg acpi_max_osi >= OSI_WIN_8)
116d79a363eSpatrick return;
117d79a363eSpatrick
11876adbdb1Spirofti acpivout_get_bcl(sc);
11930345ed3Skettenis if (sc->sc_bcl_len == 0)
12030345ed3Skettenis return;
121d3747f62Spatrick
122d3747f62Spatrick sc->sc_brightness = acpivout_get_brightness(sc);
12330345ed3Skettenis
12430345ed3Skettenis ws_get_param = acpivout_get_param;
12530345ed3Skettenis ws_set_param = acpivout_set_param;
12676adbdb1Spirofti }
12776adbdb1Spirofti
12876adbdb1Spirofti int
acpivout_notify(struct aml_node * node,int notify,void * arg)12976adbdb1Spirofti acpivout_notify(struct aml_node *node, int notify, void *arg)
13076adbdb1Spirofti {
13176adbdb1Spirofti struct acpivout_softc *sc = arg;
13276adbdb1Spirofti
13376adbdb1Spirofti switch (notify) {
13476adbdb1Spirofti case NOTIFY_BRIGHTNESS_CYCLE:
135247c8e67Skettenis wsdisplay_brightness_cycle(NULL);
13676adbdb1Spirofti break;
13776adbdb1Spirofti case NOTIFY_BRIGHTNESS_UP:
138247c8e67Skettenis wsdisplay_brightness_step(NULL, 1);
13976adbdb1Spirofti break;
14076adbdb1Spirofti case NOTIFY_BRIGHTNESS_DOWN:
141247c8e67Skettenis wsdisplay_brightness_step(NULL, -1);
14276adbdb1Spirofti break;
14376adbdb1Spirofti case NOTIFY_BRIGHTNESS_ZERO:
144247c8e67Skettenis wsdisplay_brightness_zero(NULL);
14576adbdb1Spirofti break;
14676adbdb1Spirofti case NOTIFY_DISPLAY_OFF:
14776adbdb1Spirofti /* TODO: D3 state change */
14876adbdb1Spirofti break;
14976adbdb1Spirofti default:
15076adbdb1Spirofti printf("%s: unknown event 0x%02x\n", DEVNAME(sc), notify);
15176adbdb1Spirofti break;
15276adbdb1Spirofti }
15376adbdb1Spirofti
15476adbdb1Spirofti return (0);
15576adbdb1Spirofti }
15676adbdb1Spirofti
15776adbdb1Spirofti
15876adbdb1Spirofti int
acpivout_get_brightness(struct acpivout_softc * sc)15976adbdb1Spirofti acpivout_get_brightness(struct acpivout_softc *sc)
16076adbdb1Spirofti {
16176adbdb1Spirofti struct aml_value res;
16276adbdb1Spirofti int level;
16376adbdb1Spirofti
16476adbdb1Spirofti aml_evalname(sc->sc_acpi, sc->sc_devnode, "_BQC", 0, NULL, &res);
16576adbdb1Spirofti level = aml_val2int(&res);
16676adbdb1Spirofti aml_freevalue(&res);
167d602a597Smiod DPRINTF(("%s: BQC = %d\n", DEVNAME(sc), level));
16876adbdb1Spirofti
16927e3046bSratchov if (level < sc->sc_bcl[0])
17027e3046bSratchov level = sc->sc_bcl[0];
17127e3046bSratchov else if (level > sc->sc_bcl[sc->sc_bcl_len - 1])
17227e3046bSratchov level = sc->sc_bcl[sc->sc_bcl_len - 1];
173d602a597Smiod
17476adbdb1Spirofti return (level);
17576adbdb1Spirofti }
17676adbdb1Spirofti
1779d125c60Smatthieu int
acpivout_select_brightness(struct acpivout_softc * sc,int nlevel)178d3747f62Spatrick acpivout_select_brightness(struct acpivout_softc *sc, int nlevel)
179d3747f62Spatrick {
180d3747f62Spatrick int nindex, level;
181d3747f62Spatrick
182d3747f62Spatrick level = sc->sc_brightness;
183d3747f62Spatrick nindex = acpivout_find_brightness(sc, nlevel);
184d3747f62Spatrick if (sc->sc_bcl[nindex] == level) {
185d3747f62Spatrick if (nlevel > level && (nindex + 1 < sc->sc_bcl_len))
186d3747f62Spatrick nindex++;
187d3747f62Spatrick else if (nlevel < level && (nindex - 1 >= 0))
188d3747f62Spatrick nindex--;
189d3747f62Spatrick }
190d3747f62Spatrick
191d3747f62Spatrick return nindex;
192d3747f62Spatrick }
193d3747f62Spatrick
194d3747f62Spatrick int
acpivout_find_brightness(struct acpivout_softc * sc,int level)1959d125c60Smatthieu acpivout_find_brightness(struct acpivout_softc *sc, int level)
1969d125c60Smatthieu {
1979d125c60Smatthieu int i, mid;
1989d125c60Smatthieu
1999d125c60Smatthieu for (i = 0; i < sc->sc_bcl_len - 1; i++) {
2009d125c60Smatthieu mid = sc->sc_bcl[i] + (sc->sc_bcl[i + 1] - sc->sc_bcl[i]) / 2;
2019d125c60Smatthieu if (sc->sc_bcl[i] <= level && level <= mid)
202ba8c59b4Skn return i;
2039d125c60Smatthieu if (mid < level && level <= sc->sc_bcl[i + 1])
204ba8c59b4Skn return i + 1;
2059d125c60Smatthieu }
206299d5726Skettenis if (level < sc->sc_bcl[0])
207ba8c59b4Skn return 0;
208299d5726Skettenis else
209ba8c59b4Skn return i;
2109d125c60Smatthieu }
2119d125c60Smatthieu
21276adbdb1Spirofti void
acpivout_set_brightness(void * arg0,int arg1)213d3747f62Spatrick acpivout_set_brightness(void *arg0, int arg1)
21476adbdb1Spirofti {
215d3747f62Spatrick struct acpivout_softc *sc = arg0;
21676adbdb1Spirofti struct aml_value args, res;
21776adbdb1Spirofti
21876adbdb1Spirofti memset(&args, 0, sizeof(args));
219d3747f62Spatrick args.v_integer = sc->sc_brightness;
22076adbdb1Spirofti args.type = AML_OBJTYPE_INTEGER;
22176adbdb1Spirofti
222d3747f62Spatrick DPRINTF(("%s: BCM = %d\n", DEVNAME(sc), sc->sc_brightness));
22376adbdb1Spirofti aml_evalname(sc->sc_acpi, sc->sc_devnode, "_BCM", 1, &args, &res);
22476adbdb1Spirofti
22576adbdb1Spirofti aml_freevalue(&res);
22676adbdb1Spirofti }
22776adbdb1Spirofti
22876adbdb1Spirofti void
acpivout_get_bcl(struct acpivout_softc * sc)22976adbdb1Spirofti acpivout_get_bcl(struct acpivout_softc *sc)
23076adbdb1Spirofti {
23176adbdb1Spirofti int i, j, value;
23276adbdb1Spirofti struct aml_value res;
23376adbdb1Spirofti
23476adbdb1Spirofti DPRINTF(("Getting _BCL!"));
23576adbdb1Spirofti aml_evalname(sc->sc_acpi, sc->sc_devnode, "_BCL", 0, NULL, &res);
23676adbdb1Spirofti if (res.type != AML_OBJTYPE_PACKAGE) {
23776adbdb1Spirofti sc->sc_bcl_len = 0;
23876adbdb1Spirofti goto err;
23976adbdb1Spirofti }
24076adbdb1Spirofti /*
24176adbdb1Spirofti * Per the ACPI spec section B.6.2 the _BCL method returns a package.
24276adbdb1Spirofti * The first integer in the package is the brightness level
24376adbdb1Spirofti * when the computer has full power, and the second is the
24476adbdb1Spirofti * brightness level when the computer is on batteries.
24576adbdb1Spirofti * All other levels may be used by OSPM.
24676adbdb1Spirofti * So we skip the first two integers in the package.
24776adbdb1Spirofti */
24876adbdb1Spirofti if (res.length <= 2) {
24976adbdb1Spirofti sc->sc_bcl_len = 0;
25076adbdb1Spirofti goto err;
25176adbdb1Spirofti }
25276adbdb1Spirofti sc->sc_bcl_len = res.length - 2;
25376adbdb1Spirofti
2545714159aSdoug sc->sc_bcl = mallocarray(sc->sc_bcl_len, sizeof(int), M_DEVBUF,
25576adbdb1Spirofti M_WAITOK | M_ZERO);
25676adbdb1Spirofti
25776adbdb1Spirofti for (i = 0; i < sc->sc_bcl_len; i++) {
25876adbdb1Spirofti /* Sort darkest to brightest */
25976adbdb1Spirofti value = aml_val2int(res.v_package[i + 2]);
26076adbdb1Spirofti for (j = i; j > 0 && sc->sc_bcl[j - 1] > value; j--)
26176adbdb1Spirofti sc->sc_bcl[j] = sc->sc_bcl[j - 1];
26276adbdb1Spirofti sc->sc_bcl[j] = value;
26376adbdb1Spirofti }
26476adbdb1Spirofti
26576adbdb1Spirofti err:
26676adbdb1Spirofti aml_freevalue(&res);
26776adbdb1Spirofti }
26876adbdb1Spirofti
26976adbdb1Spirofti
27076adbdb1Spirofti int
acpivout_get_param(struct wsdisplay_param * dp)27176adbdb1Spirofti acpivout_get_param(struct wsdisplay_param *dp)
27276adbdb1Spirofti {
27376adbdb1Spirofti struct acpivout_softc *sc = NULL;
27476adbdb1Spirofti int i;
27576adbdb1Spirofti
27676adbdb1Spirofti switch (dp->param) {
27776adbdb1Spirofti case WSDISPLAYIO_PARAM_BRIGHTNESS:
27876adbdb1Spirofti for (i = 0; i < acpivout_cd.cd_ndevs; i++) {
27976adbdb1Spirofti if (acpivout_cd.cd_devs[i] == NULL)
28076adbdb1Spirofti continue;
28176adbdb1Spirofti sc = (struct acpivout_softc *)acpivout_cd.cd_devs[i];
282d602a597Smiod /* Ignore device if not connected. */
28376adbdb1Spirofti if (sc->sc_bcl_len != 0)
28476adbdb1Spirofti break;
28576adbdb1Spirofti }
28676adbdb1Spirofti if (sc != NULL && sc->sc_bcl_len != 0) {
287299d5726Skettenis dp->min = 0;
28876adbdb1Spirofti dp->max = sc->sc_bcl[sc->sc_bcl_len - 1];
289d3747f62Spatrick dp->curval = sc->sc_brightness;
29076adbdb1Spirofti return 0;
29176adbdb1Spirofti }
29276adbdb1Spirofti return -1;
29376adbdb1Spirofti default:
29476adbdb1Spirofti return -1;
29576adbdb1Spirofti }
29676adbdb1Spirofti }
29776adbdb1Spirofti
29876adbdb1Spirofti int
acpivout_set_param(struct wsdisplay_param * dp)29976adbdb1Spirofti acpivout_set_param(struct wsdisplay_param *dp)
30076adbdb1Spirofti {
30176adbdb1Spirofti struct acpivout_softc *sc = NULL;
302ba8c59b4Skn int i, nindex;
30376adbdb1Spirofti
30476adbdb1Spirofti switch (dp->param) {
30576adbdb1Spirofti case WSDISPLAYIO_PARAM_BRIGHTNESS:
30676adbdb1Spirofti for (i = 0; i < acpivout_cd.cd_ndevs; i++) {
30776adbdb1Spirofti if (acpivout_cd.cd_devs[i] == NULL)
30876adbdb1Spirofti continue;
30976adbdb1Spirofti sc = (struct acpivout_softc *)acpivout_cd.cd_devs[i];
310d602a597Smiod /* Ignore device if not connected. */
31176adbdb1Spirofti if (sc->sc_bcl_len != 0)
31276adbdb1Spirofti break;
31376adbdb1Spirofti }
31476adbdb1Spirofti if (sc != NULL && sc->sc_bcl_len != 0) {
315d3747f62Spatrick nindex = acpivout_select_brightness(sc, dp->curval);
316d3747f62Spatrick sc->sc_brightness = sc->sc_bcl[nindex];
317d3747f62Spatrick acpi_addtask(sc->sc_acpi,
318d3747f62Spatrick acpivout_set_brightness, sc, 0);
319d3747f62Spatrick acpi_wakeup(sc->sc_acpi);
32076adbdb1Spirofti return 0;
32176adbdb1Spirofti }
32276adbdb1Spirofti return -1;
32376adbdb1Spirofti default:
32476adbdb1Spirofti return -1;
32576adbdb1Spirofti }
32676adbdb1Spirofti }
327