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