xref: /openbsd/sys/dev/fdt/pwmbl.c (revision 528d06d0)
1 /*	$OpenBSD: pwmbl.c,v 1.8 2023/04/25 11:21:01 patrick Exp $	*/
2 /*
3  * Copyright (c) 2019 Krystian Lewandowski
4  * Copyright (c) 2019 Mark Kettenis <kettenis@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 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/device.h>
22 #include <sys/malloc.h>
23 
24 #include <machine/fdt.h>
25 #include <machine/bus.h>
26 
27 #include <dev/ofw/openfirm.h>
28 #include <dev/ofw/ofw_gpio.h>
29 #include <dev/ofw/ofw_misc.h>
30 
31 #include <dev/wscons/wsconsio.h>
32 #include <dev/wscons/wsdisplayvar.h>
33 
34 struct pwmbl_softc {
35 	struct device		sc_dev;
36 	uint32_t		*sc_pwm;
37 	int			sc_pwm_len;
38 	uint32_t		*sc_levels;	/* NULL if simple ramp */
39 	int			sc_nlevels;
40 	uint32_t		sc_max_level;
41 	uint32_t		sc_def_level;
42 	struct pwm_state	sc_ps_saved;
43 };
44 
45 struct pwmbl_softc *sc_pwmbl;
46 
47 int	pwmbl_match(struct device *, void *, void *);
48 void	pwmbl_attach(struct device *, struct device *, void *);
49 int	pwmbl_activate(struct device *, int);
50 
51 const struct cfattach pwmbl_ca = {
52 	sizeof(struct pwmbl_softc), pwmbl_match, pwmbl_attach, NULL,
53 	pwmbl_activate
54 };
55 
56 struct cfdriver pwmbl_cd = {
57 	NULL, "pwmbl", DV_DULL
58 };
59 
60 int	pwmbl_get_brightness(void *, uint32_t *);
61 int	pwmbl_set_brightness(void *, uint32_t);
62 int	pwmbl_get_param(struct wsdisplay_param *);
63 int	pwmbl_set_param(struct wsdisplay_param *);
64 
65 int
pwmbl_match(struct device * parent,void * match,void * aux)66 pwmbl_match(struct device *parent, void *match, void *aux)
67 {
68 	struct fdt_attach_args *faa = aux;
69 
70 	return OF_is_compatible(faa->fa_node, "pwm-backlight");
71 }
72 
73 void
pwmbl_attach(struct device * parent,struct device * self,void * aux)74 pwmbl_attach(struct device *parent, struct device *self, void *aux)
75 {
76 	struct pwmbl_softc *sc = (struct pwmbl_softc *)self;
77 	struct fdt_attach_args *faa = aux;
78 	uint32_t *gpios;
79 	int len;
80 
81 	len = OF_getproplen(faa->fa_node, "pwms");
82 	if (len < 0) {
83 		printf(": no pwm\n");
84 		return;
85 	}
86 
87 	sc->sc_pwm = malloc(len, M_DEVBUF, M_WAITOK);
88 	OF_getpropintarray(faa->fa_node, "pwms", sc->sc_pwm, len);
89 	sc->sc_pwm_len = len;
90 
91 	len = OF_getproplen(faa->fa_node, "enable-gpios");
92 	if (len > 0) {
93 		gpios = malloc(len, M_TEMP, M_WAITOK);
94 		OF_getpropintarray(faa->fa_node, "enable-gpios", gpios, len);
95 		gpio_controller_config_pin(&gpios[0], GPIO_CONFIG_OUTPUT);
96 		gpio_controller_set_pin(&gpios[0], 1);
97 		free(gpios, M_TEMP, len);
98 	}
99 
100 	len = OF_getproplen(faa->fa_node, "brightness-levels");
101 	if (len >= (int)sizeof(uint32_t)) {
102 		sc->sc_levels = malloc(len, M_DEVBUF, M_WAITOK);
103 		OF_getpropintarray(faa->fa_node, "brightness-levels",
104 		    sc->sc_levels, len);
105 		sc->sc_nlevels = len / sizeof(uint32_t);
106 		sc->sc_max_level = sc->sc_levels[sc->sc_nlevels - 1];
107 		sc->sc_def_level = OF_getpropint(faa->fa_node,
108 		    "default-brightness-level", sc->sc_nlevels - 1);
109 		if (sc->sc_def_level >= sc->sc_nlevels)
110 			sc->sc_def_level = sc->sc_nlevels - 1;
111 		sc->sc_def_level = sc->sc_levels[sc->sc_def_level];
112 	} else {
113 		/* No levels, assume a simple 0..255 ramp. */
114 		sc->sc_nlevels = 256;
115 		sc->sc_max_level = sc->sc_def_level = sc->sc_nlevels - 1;
116 	}
117 
118 	printf("\n");
119 
120 	pwmbl_set_brightness(sc, sc->sc_def_level);
121 
122 	sc_pwmbl = sc;
123 	ws_get_param = pwmbl_get_param;
124 	ws_set_param = pwmbl_set_param;
125 }
126 
127 int
pwmbl_activate(struct device * self,int act)128 pwmbl_activate(struct device *self, int act)
129 {
130 	struct pwmbl_softc *sc = (struct pwmbl_softc *)self;
131 	struct pwm_state ps;
132 	int error;
133 
134 	switch (act) {
135 	case DVACT_QUIESCE:
136 		error = pwm_get_state(sc->sc_pwm, &sc->sc_ps_saved);
137 		if (error)
138 			return error;
139 
140 		pwm_init_state(sc->sc_pwm, &ps);
141 		ps.ps_pulse_width = 0;
142 		ps.ps_enabled = 0;
143 		return pwm_set_state(sc->sc_pwm, &ps);
144 	case DVACT_WAKEUP:
145 		return pwm_set_state(sc->sc_pwm, &sc->sc_ps_saved);
146 	}
147 	return 0;
148 }
149 
150 int
pwmbl_get_brightness(void * cookie,uint32_t * level)151 pwmbl_get_brightness(void *cookie, uint32_t *level)
152 {
153 	struct pwmbl_softc *sc = cookie;
154 	struct pwm_state ps;
155 
156 	if (pwm_get_state(sc->sc_pwm, &ps))
157 		return EINVAL;
158 
159 	*level = (ps.ps_pulse_width * sc->sc_max_level) / ps.ps_period;
160 	return 0;
161 }
162 
163 uint32_t
pwmbl_find_brightness(struct pwmbl_softc * sc,uint32_t level)164 pwmbl_find_brightness(struct pwmbl_softc *sc, uint32_t level)
165 {
166 	uint32_t mid;
167 	int i;
168 
169 	if (sc->sc_levels == NULL)
170 		return level < sc->sc_nlevels ? level : sc->sc_nlevels - 1;
171 
172 	for (i = 0; i < sc->sc_nlevels - 1; i++) {
173 		mid = (sc->sc_levels[i] + sc->sc_levels[i + 1]) / 2;
174 		if (sc->sc_levels[i] <= level && level <= mid)
175 			return sc->sc_levels[i];
176 		if (mid < level && level <= sc->sc_levels[i + 1])
177 			return sc->sc_levels[i + 1];
178 	}
179 	if (level < sc->sc_levels[0])
180 		return sc->sc_levels[0];
181 	else
182 		return sc->sc_levels[i];
183 }
184 
185 int
pwmbl_set_brightness(void * cookie,uint32_t level)186 pwmbl_set_brightness(void *cookie, uint32_t level)
187 {
188 	struct pwmbl_softc *sc = cookie;
189 	struct pwm_state ps;
190 
191 	if (pwm_init_state(sc->sc_pwm, &ps))
192 		return EINVAL;
193 
194 	level = pwmbl_find_brightness(sc, level);
195 
196 	ps.ps_enabled = 1;
197 	ps.ps_pulse_width = (ps.ps_period * level) / sc->sc_max_level;
198 	return pwm_set_state(sc->sc_pwm, &ps);
199 }
200 
201 int
pwmbl_get_param(struct wsdisplay_param * dp)202 pwmbl_get_param(struct wsdisplay_param *dp)
203 {
204 	struct pwmbl_softc *sc = (struct pwmbl_softc *)sc_pwmbl;
205 	uint32_t level;
206 
207 	switch (dp->param) {
208 	case WSDISPLAYIO_PARAM_BRIGHTNESS:
209 		if (pwmbl_get_brightness(sc, &level))
210 			return -1;
211 
212 		dp->min = 0;
213 		dp->max = sc->sc_max_level;
214 		dp->curval = level;
215 		return 0;
216 	default:
217 		return -1;
218 	}
219 }
220 
221 int
pwmbl_set_param(struct wsdisplay_param * dp)222 pwmbl_set_param(struct wsdisplay_param *dp)
223 {
224 	struct pwmbl_softc *sc = (struct pwmbl_softc *)sc_pwmbl;
225 
226 	switch (dp->param) {
227 	case WSDISPLAYIO_PARAM_BRIGHTNESS:
228 		if (pwmbl_set_brightness(sc, dp->curval))
229 			return -1;
230 		return 0;
231 	default:
232 		return -1;
233 	}
234 }
235