xref: /openbsd-src/sys/dev/acpi/acpivout.c (revision 3b9d585e0ce86bc6eed0b4493f3c73d741778f91)
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