xref: /openbsd-src/sys/dev/fdt/pwmbl.c (revision 528d06d0bc229d08a2b877bcd430e58682e03258)
1*528d06d0Spatrick /*	$OpenBSD: pwmbl.c,v 1.8 2023/04/25 11:21:01 patrick Exp $	*/
2b2021c38Skettenis /*
3b2021c38Skettenis  * Copyright (c) 2019 Krystian Lewandowski
4b2021c38Skettenis  * Copyright (c) 2019 Mark Kettenis <kettenis@openbsd.org>
5b2021c38Skettenis  *
6b2021c38Skettenis  * Permission to use, copy, modify, and distribute this software for any
7b2021c38Skettenis  * purpose with or without fee is hereby granted, provided that the above
8b2021c38Skettenis  * copyright notice and this permission notice appear in all copies.
9b2021c38Skettenis  *
10b2021c38Skettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11b2021c38Skettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12b2021c38Skettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13b2021c38Skettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14b2021c38Skettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15b2021c38Skettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16b2021c38Skettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17b2021c38Skettenis  */
18b2021c38Skettenis 
19b2021c38Skettenis #include <sys/param.h>
20b2021c38Skettenis #include <sys/systm.h>
21b2021c38Skettenis #include <sys/device.h>
22b2021c38Skettenis #include <sys/malloc.h>
23b2021c38Skettenis 
24b2021c38Skettenis #include <machine/fdt.h>
25b2021c38Skettenis #include <machine/bus.h>
26b2021c38Skettenis 
27b2021c38Skettenis #include <dev/ofw/openfirm.h>
28b2021c38Skettenis #include <dev/ofw/ofw_gpio.h>
29b2021c38Skettenis #include <dev/ofw/ofw_misc.h>
30b2021c38Skettenis 
31b2021c38Skettenis #include <dev/wscons/wsconsio.h>
32b2021c38Skettenis #include <dev/wscons/wsdisplayvar.h>
33b2021c38Skettenis 
34b2021c38Skettenis struct pwmbl_softc {
35b2021c38Skettenis 	struct device		sc_dev;
36b2021c38Skettenis 	uint32_t		*sc_pwm;
37b2021c38Skettenis 	int			sc_pwm_len;
3804b807ceSmiod 	uint32_t		*sc_levels;	/* NULL if simple ramp */
39b2021c38Skettenis 	int			sc_nlevels;
40b2021c38Skettenis 	uint32_t		sc_max_level;
41b2021c38Skettenis 	uint32_t		sc_def_level;
42*528d06d0Spatrick 	struct pwm_state	sc_ps_saved;
43b2021c38Skettenis };
44b2021c38Skettenis 
45b2021c38Skettenis struct pwmbl_softc *sc_pwmbl;
46b2021c38Skettenis 
47b2021c38Skettenis int	pwmbl_match(struct device *, void *, void *);
48b2021c38Skettenis void	pwmbl_attach(struct device *, struct device *, void *);
49*528d06d0Spatrick int	pwmbl_activate(struct device *, int);
50b2021c38Skettenis 
519fdf0c62Smpi const struct cfattach pwmbl_ca = {
52*528d06d0Spatrick 	sizeof(struct pwmbl_softc), pwmbl_match, pwmbl_attach, NULL,
53*528d06d0Spatrick 	pwmbl_activate
54b2021c38Skettenis };
55b2021c38Skettenis 
56b2021c38Skettenis struct cfdriver pwmbl_cd = {
57b2021c38Skettenis 	NULL, "pwmbl", DV_DULL
58b2021c38Skettenis };
59b2021c38Skettenis 
60b2021c38Skettenis int	pwmbl_get_brightness(void *, uint32_t *);
61b2021c38Skettenis int	pwmbl_set_brightness(void *, uint32_t);
62b2021c38Skettenis int	pwmbl_get_param(struct wsdisplay_param *);
63b2021c38Skettenis int	pwmbl_set_param(struct wsdisplay_param *);
64b2021c38Skettenis 
65b2021c38Skettenis int
pwmbl_match(struct device * parent,void * match,void * aux)66b2021c38Skettenis pwmbl_match(struct device *parent, void *match, void *aux)
67b2021c38Skettenis {
68b2021c38Skettenis 	struct fdt_attach_args *faa = aux;
69b2021c38Skettenis 
70b2021c38Skettenis 	return OF_is_compatible(faa->fa_node, "pwm-backlight");
71b2021c38Skettenis }
72b2021c38Skettenis 
73b2021c38Skettenis void
pwmbl_attach(struct device * parent,struct device * self,void * aux)74b2021c38Skettenis pwmbl_attach(struct device *parent, struct device *self, void *aux)
75b2021c38Skettenis {
76b2021c38Skettenis 	struct pwmbl_softc *sc = (struct pwmbl_softc *)self;
77b2021c38Skettenis 	struct fdt_attach_args *faa = aux;
78b2021c38Skettenis 	uint32_t *gpios;
7904b807ceSmiod 	int len;
80b2021c38Skettenis 
81b2021c38Skettenis 	len = OF_getproplen(faa->fa_node, "pwms");
82b2021c38Skettenis 	if (len < 0) {
83b2021c38Skettenis 		printf(": no pwm\n");
84b2021c38Skettenis 		return;
85b2021c38Skettenis 	}
86b2021c38Skettenis 
87b2021c38Skettenis 	sc->sc_pwm = malloc(len, M_DEVBUF, M_WAITOK);
88b2021c38Skettenis 	OF_getpropintarray(faa->fa_node, "pwms", sc->sc_pwm, len);
89b2021c38Skettenis 	sc->sc_pwm_len = len;
90b2021c38Skettenis 
91b2021c38Skettenis 	len = OF_getproplen(faa->fa_node, "enable-gpios");
92feb18c6cSpatrick 	if (len > 0) {
93b2021c38Skettenis 		gpios = malloc(len, M_TEMP, M_WAITOK);
94b2021c38Skettenis 		OF_getpropintarray(faa->fa_node, "enable-gpios", gpios, len);
95b2021c38Skettenis 		gpio_controller_config_pin(&gpios[0], GPIO_CONFIG_OUTPUT);
96b2021c38Skettenis 		gpio_controller_set_pin(&gpios[0], 1);
97b2021c38Skettenis 		free(gpios, M_TEMP, len);
98feb18c6cSpatrick 	}
99b2021c38Skettenis 
100b2021c38Skettenis 	len = OF_getproplen(faa->fa_node, "brightness-levels");
10104b807ceSmiod 	if (len >= (int)sizeof(uint32_t)) {
102b2021c38Skettenis 		sc->sc_levels = malloc(len, M_DEVBUF, M_WAITOK);
103b2021c38Skettenis 		OF_getpropintarray(faa->fa_node, "brightness-levels",
104b2021c38Skettenis 		    sc->sc_levels, len);
105b2021c38Skettenis 		sc->sc_nlevels = len / sizeof(uint32_t);
106b2021c38Skettenis 		sc->sc_max_level = sc->sc_levels[sc->sc_nlevels - 1];
107b2021c38Skettenis 		sc->sc_def_level = OF_getpropint(faa->fa_node,
1089531bc94Spatrick 		    "default-brightness-level", sc->sc_nlevels - 1);
1099531bc94Spatrick 		if (sc->sc_def_level >= sc->sc_nlevels)
1109531bc94Spatrick 			sc->sc_def_level = sc->sc_nlevels - 1;
1119531bc94Spatrick 		sc->sc_def_level = sc->sc_levels[sc->sc_def_level];
112af60f639Spatrick 	} else {
11304b807ceSmiod 		/* No levels, assume a simple 0..255 ramp. */
114af60f639Spatrick 		sc->sc_nlevels = 256;
11504b807ceSmiod 		sc->sc_max_level = sc->sc_def_level = sc->sc_nlevels - 1;
116af60f639Spatrick 	}
117af60f639Spatrick 
118af60f639Spatrick 	printf("\n");
119b2021c38Skettenis 
120543b388eSpatrick 	pwmbl_set_brightness(sc, sc->sc_def_level);
121543b388eSpatrick 
122b2021c38Skettenis 	sc_pwmbl = sc;
123b2021c38Skettenis 	ws_get_param = pwmbl_get_param;
124b2021c38Skettenis 	ws_set_param = pwmbl_set_param;
125b2021c38Skettenis }
126b2021c38Skettenis 
127b2021c38Skettenis int
pwmbl_activate(struct device * self,int act)128*528d06d0Spatrick pwmbl_activate(struct device *self, int act)
129*528d06d0Spatrick {
130*528d06d0Spatrick 	struct pwmbl_softc *sc = (struct pwmbl_softc *)self;
131*528d06d0Spatrick 	struct pwm_state ps;
132*528d06d0Spatrick 	int error;
133*528d06d0Spatrick 
134*528d06d0Spatrick 	switch (act) {
135*528d06d0Spatrick 	case DVACT_QUIESCE:
136*528d06d0Spatrick 		error = pwm_get_state(sc->sc_pwm, &sc->sc_ps_saved);
137*528d06d0Spatrick 		if (error)
138*528d06d0Spatrick 			return error;
139*528d06d0Spatrick 
140*528d06d0Spatrick 		pwm_init_state(sc->sc_pwm, &ps);
141*528d06d0Spatrick 		ps.ps_pulse_width = 0;
142*528d06d0Spatrick 		ps.ps_enabled = 0;
143*528d06d0Spatrick 		return pwm_set_state(sc->sc_pwm, &ps);
144*528d06d0Spatrick 	case DVACT_WAKEUP:
145*528d06d0Spatrick 		return pwm_set_state(sc->sc_pwm, &sc->sc_ps_saved);
146*528d06d0Spatrick 	}
147*528d06d0Spatrick 	return 0;
148*528d06d0Spatrick }
149*528d06d0Spatrick 
150*528d06d0Spatrick int
pwmbl_get_brightness(void * cookie,uint32_t * level)151b2021c38Skettenis pwmbl_get_brightness(void *cookie, uint32_t *level)
152b2021c38Skettenis {
153b2021c38Skettenis 	struct pwmbl_softc *sc = cookie;
154b2021c38Skettenis 	struct pwm_state ps;
155b2021c38Skettenis 
156b2021c38Skettenis 	if (pwm_get_state(sc->sc_pwm, &ps))
157b2021c38Skettenis 		return EINVAL;
158b2021c38Skettenis 
159b2021c38Skettenis 	*level = (ps.ps_pulse_width * sc->sc_max_level) / ps.ps_period;
160b2021c38Skettenis 	return 0;
161b2021c38Skettenis }
162b2021c38Skettenis 
163b2021c38Skettenis uint32_t
pwmbl_find_brightness(struct pwmbl_softc * sc,uint32_t level)164b2021c38Skettenis pwmbl_find_brightness(struct pwmbl_softc *sc, uint32_t level)
165b2021c38Skettenis {
166b2021c38Skettenis 	uint32_t mid;
167b2021c38Skettenis 	int i;
168b2021c38Skettenis 
16904b807ceSmiod 	if (sc->sc_levels == NULL)
17004b807ceSmiod 		return level < sc->sc_nlevels ? level : sc->sc_nlevels - 1;
17104b807ceSmiod 
172b2021c38Skettenis 	for (i = 0; i < sc->sc_nlevels - 1; i++) {
173b2021c38Skettenis 		mid = (sc->sc_levels[i] + sc->sc_levels[i + 1]) / 2;
174b2021c38Skettenis 		if (sc->sc_levels[i] <= level && level <= mid)
175b2021c38Skettenis 			return sc->sc_levels[i];
176b2021c38Skettenis 		if (mid < level && level <= sc->sc_levels[i + 1])
177b2021c38Skettenis 			return sc->sc_levels[i + 1];
178b2021c38Skettenis 	}
179b2021c38Skettenis 	if (level < sc->sc_levels[0])
180b2021c38Skettenis 		return sc->sc_levels[0];
181b2021c38Skettenis 	else
182b2021c38Skettenis 		return sc->sc_levels[i];
183b2021c38Skettenis }
184b2021c38Skettenis 
185b2021c38Skettenis int
pwmbl_set_brightness(void * cookie,uint32_t level)186b2021c38Skettenis pwmbl_set_brightness(void *cookie, uint32_t level)
187b2021c38Skettenis {
188b2021c38Skettenis 	struct pwmbl_softc *sc = cookie;
189b2021c38Skettenis 	struct pwm_state ps;
190b2021c38Skettenis 
191b2021c38Skettenis 	if (pwm_init_state(sc->sc_pwm, &ps))
192b2021c38Skettenis 		return EINVAL;
193b2021c38Skettenis 
194b2021c38Skettenis 	level = pwmbl_find_brightness(sc, level);
195b2021c38Skettenis 
196b2021c38Skettenis 	ps.ps_enabled = 1;
197b2021c38Skettenis 	ps.ps_pulse_width = (ps.ps_period * level) / sc->sc_max_level;
198b2021c38Skettenis 	return pwm_set_state(sc->sc_pwm, &ps);
199b2021c38Skettenis }
200b2021c38Skettenis 
201b2021c38Skettenis int
pwmbl_get_param(struct wsdisplay_param * dp)202b2021c38Skettenis pwmbl_get_param(struct wsdisplay_param *dp)
203b2021c38Skettenis {
204b2021c38Skettenis 	struct pwmbl_softc *sc = (struct pwmbl_softc *)sc_pwmbl;
205b2021c38Skettenis 	uint32_t level;
206b2021c38Skettenis 
207b2021c38Skettenis 	switch (dp->param) {
208b2021c38Skettenis 	case WSDISPLAYIO_PARAM_BRIGHTNESS:
209b2021c38Skettenis 		if (pwmbl_get_brightness(sc, &level))
210b2021c38Skettenis 			return -1;
211b2021c38Skettenis 
212b2021c38Skettenis 		dp->min = 0;
213b2021c38Skettenis 		dp->max = sc->sc_max_level;
214b2021c38Skettenis 		dp->curval = level;
215b2021c38Skettenis 		return 0;
216b2021c38Skettenis 	default:
217b2021c38Skettenis 		return -1;
218b2021c38Skettenis 	}
219b2021c38Skettenis }
220b2021c38Skettenis 
221b2021c38Skettenis int
pwmbl_set_param(struct wsdisplay_param * dp)222b2021c38Skettenis pwmbl_set_param(struct wsdisplay_param *dp)
223b2021c38Skettenis {
224b2021c38Skettenis 	struct pwmbl_softc *sc = (struct pwmbl_softc *)sc_pwmbl;
225b2021c38Skettenis 
226b2021c38Skettenis 	switch (dp->param) {
227b2021c38Skettenis 	case WSDISPLAYIO_PARAM_BRIGHTNESS:
228b2021c38Skettenis 		if (pwmbl_set_brightness(sc, dp->curval))
229b2021c38Skettenis 			return -1;
230b2021c38Skettenis 		return 0;
231b2021c38Skettenis 	default:
232b2021c38Skettenis 		return -1;
233b2021c38Skettenis 	}
234b2021c38Skettenis }
235