xref: /openbsd-src/sys/dev/acpi/abl.c (revision eb3d9e2dd0357d3826bf04a6468e649c8525d43e)
1*eb3d9e2dSkettenis /*	$OpenBSD: abl.c,v 1.6 2025/01/23 11:24:34 kettenis Exp $ */
2c34968d9Smglocker 
3c34968d9Smglocker /*
4c34968d9Smglocker  * Copyright (c) 2020 Marcus Glocker <mglocker@openbsd.org>
5c34968d9Smglocker  *
6c34968d9Smglocker  * Permission to use, copy, modify, and distribute this software for any
7c34968d9Smglocker  * purpose with or without fee is hereby granted, provided that the above
8c34968d9Smglocker  * copyright notice and this permission notice appear in all copies.
9c34968d9Smglocker  *
10c34968d9Smglocker  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11c34968d9Smglocker  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12c34968d9Smglocker  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13c34968d9Smglocker  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14c34968d9Smglocker  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15c34968d9Smglocker  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16c34968d9Smglocker  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17c34968d9Smglocker  */
18c34968d9Smglocker 
19c34968d9Smglocker /*
20c34968d9Smglocker  * Driver for controlling the screen backlight brightness on Apple machines.
21c34968d9Smglocker  */
22c34968d9Smglocker #include <sys/param.h>
23c34968d9Smglocker #include <sys/systm.h>
24c34968d9Smglocker 
25c34968d9Smglocker #include <dev/acpi/acpivar.h>
26c34968d9Smglocker #include <dev/acpi/acpidev.h>
27c34968d9Smglocker #include <dev/acpi/amltypes.h>
28c34968d9Smglocker #include <dev/acpi/dsdt.h>
29c34968d9Smglocker 
30c34968d9Smglocker #include <dev/pci/pcidevs.h>
31c34968d9Smglocker 
32c34968d9Smglocker #include <dev/wscons/wsconsio.h>
33c34968d9Smglocker #include <dev/wscons/wsdisplayvar.h>
34c34968d9Smglocker 
35c34968d9Smglocker #ifdef ABL_DEBUG
36c34968d9Smglocker #define DPRINTF(x) printf x
37c34968d9Smglocker #else
38c34968d9Smglocker #define DPRINTF(x)
39c34968d9Smglocker #endif
40c34968d9Smglocker 
41c34968d9Smglocker #define ABL_IO_BASE_INTEL	0xb2
42c34968d9Smglocker #define ABL_IO_BASE_NVIDIA	0x52e
43c34968d9Smglocker #define ABL_IO_SIZE		2
44c34968d9Smglocker #define ABL_IO_LO		0x00
45c34968d9Smglocker #define ABL_IO_HI		0x01
46c34968d9Smglocker #define ABL_BRIGHTNESS_MIN	0
47c34968d9Smglocker #define ABL_BRIGHTNESS_MAX	15
48c34968d9Smglocker 
49c34968d9Smglocker struct abl_softc {
50c34968d9Smglocker 	struct device		 sc_dev;
51c34968d9Smglocker 
52c34968d9Smglocker 	struct acpi_softc	*sc_acpi;
53c34968d9Smglocker 	struct aml_node		*sc_devnode;
54c34968d9Smglocker 
55c34968d9Smglocker 	bus_space_tag_t		 sc_bt;
56c34968d9Smglocker 	bus_space_handle_t	 sc_bh;
57c34968d9Smglocker 
58c34968d9Smglocker 	bus_addr_t		 sc_io_base;
59c34968d9Smglocker 	uint8_t			 sc_brightness;
60c34968d9Smglocker };
61c34968d9Smglocker 
62c34968d9Smglocker int	abl_match(struct device *, void *, void *);
63c34968d9Smglocker void	abl_attach(struct device *, struct device *, void *);
64c34968d9Smglocker 
65c34968d9Smglocker int	abl_get_brightness(struct abl_softc *);
66c34968d9Smglocker int	abl_set_brightness(struct abl_softc *, uint8_t);
67c34968d9Smglocker 
68c34968d9Smglocker /* Hooks for wsconsole brightness control. */
69c34968d9Smglocker int	abl_get_param(struct wsdisplay_param *);
70c34968d9Smglocker int	abl_set_param(struct wsdisplay_param *);
71c34968d9Smglocker 
72471aeecfSnaddy const struct cfattach abl_ca = {
73c34968d9Smglocker 	sizeof(struct abl_softc), abl_match, abl_attach, NULL, NULL
74c34968d9Smglocker };
75c34968d9Smglocker 
76c34968d9Smglocker struct cfdriver abl_cd = {
77c34968d9Smglocker 	NULL, "abl", DV_DULL
78c34968d9Smglocker };
79c34968d9Smglocker 
80c34968d9Smglocker const char *abl_hids[] = {
81c34968d9Smglocker 	"APP0002", NULL
82c34968d9Smglocker };
83c34968d9Smglocker 
84c34968d9Smglocker int
85c34968d9Smglocker abl_match(struct device *parent, void *match, void *aux)
86c34968d9Smglocker {
87c34968d9Smglocker 	struct acpi_attach_args *aa = aux;
88c34968d9Smglocker 	struct cfdata *cf = match;
89c34968d9Smglocker 
90c34968d9Smglocker 	return acpi_matchhids(aa, abl_hids, cf->cf_driver->cd_name);
91c34968d9Smglocker }
92c34968d9Smglocker 
93c34968d9Smglocker void
94c34968d9Smglocker abl_attach(struct device *parent, struct device *self, void *aux)
95c34968d9Smglocker {
96c34968d9Smglocker 	struct abl_softc *sc = (struct abl_softc *)self;
97c34968d9Smglocker 	struct acpi_attach_args *aaa = aux;
98c34968d9Smglocker 	struct aml_value res;
99c34968d9Smglocker 	int64_t sta;
100c34968d9Smglocker 	int reg;
101c34968d9Smglocker 	pci_chipset_tag_t pc;
102c34968d9Smglocker 	pcitag_t tag;
103c34968d9Smglocker 
104c34968d9Smglocker 	sc->sc_acpi = (struct acpi_softc *)parent;
105c34968d9Smglocker 	sc->sc_devnode = aaa->aaa_node;
106c34968d9Smglocker 
107c34968d9Smglocker 	printf(": %s", sc->sc_devnode->name);
108c34968d9Smglocker 
109c34968d9Smglocker         sta = acpi_getsta(sc->sc_acpi, sc->sc_devnode);
110c34968d9Smglocker 	if ((sta & (STA_PRESENT | STA_ENABLED | STA_DEV_OK)) !=
111c34968d9Smglocker 	    (STA_PRESENT | STA_ENABLED | STA_DEV_OK)) {
112c34968d9Smglocker 		printf(": not enabled\n");
113c34968d9Smglocker 		return;
114c34968d9Smglocker 	}
115c34968d9Smglocker 
116c34968d9Smglocker 	if (!(aml_evalname(sc->sc_acpi, sc->sc_devnode, "_CID", 0, NULL, &res)))
117c34968d9Smglocker 		printf(" (%s)", res.v_string);
118c34968d9Smglocker 
119f8cacb9aSmglocker 	/* Backlight on non-iMacs is already handled differently. */
120f8cacb9aSmglocker 	if (strncmp(hw_prod, "iMac", 4)) {
121f8cacb9aSmglocker 		printf("\n");
122f8cacb9aSmglocker 		return;
123f8cacb9aSmglocker 	}
124f8cacb9aSmglocker 
125c34968d9Smglocker 	/*
1264b1a56afSjsg 	 * We need to check on what type of PCI controller we're running on to
127c34968d9Smglocker 	 * access the right I/O space.
128c34968d9Smglocker 	 */
129*eb3d9e2dSkettenis 	pc = pci_lookup_segment(0, 0);
130c34968d9Smglocker 	tag = pci_make_tag(pc, 0, 0, 0);
131c34968d9Smglocker 	reg = pci_conf_read(pc, tag, PCI_ID_REG);
132c34968d9Smglocker 
133c34968d9Smglocker 	switch (PCI_VENDOR(reg)) {
134c34968d9Smglocker 	case PCI_VENDOR_INTEL:
135c34968d9Smglocker 		sc->sc_io_base = ABL_IO_BASE_INTEL;
136c34968d9Smglocker 		break;
137c34968d9Smglocker 	case PCI_VENDOR_NVIDIA:
138c34968d9Smglocker 		sc->sc_io_base = ABL_IO_BASE_NVIDIA;
139c34968d9Smglocker 		break;
140c34968d9Smglocker 	default:
141c34968d9Smglocker 		printf(": pci controller not supported\n");
142c34968d9Smglocker 		return;
143c34968d9Smglocker 	}
144c34968d9Smglocker 
145c8f65f57Skettenis 	/*
146c8f65f57Skettenis 	 * Map I/O space.  This driver uses the ACPI SMI command port.
147c8f65f57Skettenis 	 * This port has already been claimed by the generic ACPI code
148c8f65f57Skettenis 	 * so we need to work around that here by calling _bus_space_map().
149c8f65f57Skettenis 	 */
150c34968d9Smglocker 	sc->sc_bt = aaa->aaa_iot;
151c34968d9Smglocker 	if (_bus_space_map(sc->sc_bt, sc->sc_io_base, ABL_IO_SIZE, 0,
152c34968d9Smglocker 	    &sc->sc_bh)) {
153c34968d9Smglocker 		printf(": can't map register\n");
154c34968d9Smglocker 		return;
155c34968d9Smglocker 	}
156c34968d9Smglocker 
157c34968d9Smglocker 	/* Read the current brightness value initially. */
158c34968d9Smglocker 	sc->sc_brightness = abl_get_brightness(sc);
159c34968d9Smglocker 
160c34968d9Smglocker 	/* Map wsconsole hook functions. */
161c34968d9Smglocker 	ws_get_param = abl_get_param;
162c34968d9Smglocker 	ws_set_param = abl_set_param;
163c34968d9Smglocker 
164c34968d9Smglocker 	printf("\n");
165c34968d9Smglocker }
166c34968d9Smglocker 
167c34968d9Smglocker int
168c34968d9Smglocker abl_get_brightness(struct abl_softc *sc)
169c34968d9Smglocker {
170c34968d9Smglocker 	uint8_t val;
171c34968d9Smglocker 
172c34968d9Smglocker 	bus_space_write_1(sc->sc_bt, sc->sc_bh, ABL_IO_HI, 0x03);
173c34968d9Smglocker 	bus_space_write_1(sc->sc_bt, sc->sc_bh, ABL_IO_LO, 0xbf);
174c34968d9Smglocker 	val = bus_space_read_1(sc->sc_bt, sc->sc_bh, ABL_IO_HI);
175c34968d9Smglocker 
176c34968d9Smglocker 	return (val >> 4);
177c34968d9Smglocker }
178c34968d9Smglocker 
179c34968d9Smglocker int
180c34968d9Smglocker abl_set_brightness(struct abl_softc *sc, uint8_t val)
181c34968d9Smglocker {
182c34968d9Smglocker 	bus_space_write_1(sc->sc_bt, sc->sc_bh, ABL_IO_HI, 0x04 | (val << 4));
183c34968d9Smglocker 	bus_space_write_1(sc->sc_bt, sc->sc_bh, ABL_IO_LO, 0xbf);
184c34968d9Smglocker 
185c34968d9Smglocker 	return 0;
186c34968d9Smglocker }
187c34968d9Smglocker 
188c34968d9Smglocker int
189c34968d9Smglocker abl_get_param(struct wsdisplay_param *dp)
190c34968d9Smglocker {
191c34968d9Smglocker 	struct abl_softc *sc = abl_cd.cd_devs[0];
192c34968d9Smglocker 
193c34968d9Smglocker 	if (sc == NULL)
194c34968d9Smglocker 		return -1;
195c34968d9Smglocker 
196c34968d9Smglocker 	switch (dp->param) {
197c34968d9Smglocker 	case WSDISPLAYIO_PARAM_BRIGHTNESS:
198c34968d9Smglocker 		DPRINTF(("abl_get_param: sc->sc_brightness = %d\n",
199c34968d9Smglocker 		    sc->sc_brightness));
200c34968d9Smglocker 		dp->min = ABL_BRIGHTNESS_MIN;
201c34968d9Smglocker 		dp->max = ABL_BRIGHTNESS_MAX;
202c34968d9Smglocker 		dp->curval = sc->sc_brightness;
203c34968d9Smglocker 		return 0;
204c34968d9Smglocker 	default:
205c34968d9Smglocker 		return -1;
206c34968d9Smglocker 	}
207c34968d9Smglocker }
208c34968d9Smglocker 
209c34968d9Smglocker int
210c34968d9Smglocker abl_set_param(struct wsdisplay_param *dp)
211c34968d9Smglocker {
212c34968d9Smglocker 	struct abl_softc *sc = abl_cd.cd_devs[0];
213c34968d9Smglocker 
214c34968d9Smglocker 	if (sc == NULL)
215c34968d9Smglocker 		return -1;
216c34968d9Smglocker 
217c34968d9Smglocker 	switch (dp->param) {
218c34968d9Smglocker 	case WSDISPLAYIO_PARAM_BRIGHTNESS:
219c34968d9Smglocker 		DPRINTF(("abl_set_param: curval = %d\n", dp->curval));
220c34968d9Smglocker 		if (dp->curval < ABL_BRIGHTNESS_MIN)
221c34968d9Smglocker 			dp->curval = 0;
222c34968d9Smglocker 		if (dp->curval > ABL_BRIGHTNESS_MAX)
223c34968d9Smglocker 			dp->curval = ABL_BRIGHTNESS_MAX;
224c34968d9Smglocker 		abl_set_brightness(sc, dp->curval);
225c34968d9Smglocker 		sc->sc_brightness = dp->curval;
226c34968d9Smglocker 		return 0;
227c34968d9Smglocker 	default:
228c34968d9Smglocker 		return -1;
229c34968d9Smglocker 	}
230c34968d9Smglocker }
231