xref: /openbsd/sys/dev/fdt/pwmbl.c (revision 3cab2bb3)
1 /*	$OpenBSD: pwmbl.c,v 1.5 2020/06/10 23:36:26 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;
39 	int			sc_nlevels;
40 	uint32_t		sc_max_level;
41 	uint32_t		sc_def_level;
42 };
43 
44 struct pwmbl_softc *sc_pwmbl;
45 
46 int	pwmbl_match(struct device *, void *, void *);
47 void	pwmbl_attach(struct device *, struct device *, void *);
48 
49 struct cfattach pwmbl_ca = {
50 	sizeof(struct pwmbl_softc), pwmbl_match, pwmbl_attach
51 };
52 
53 struct cfdriver pwmbl_cd = {
54 	NULL, "pwmbl", DV_DULL
55 };
56 
57 int	pwmbl_get_brightness(void *, uint32_t *);
58 int	pwmbl_set_brightness(void *, uint32_t);
59 int	pwmbl_get_param(struct wsdisplay_param *);
60 int	pwmbl_set_param(struct wsdisplay_param *);
61 
62 int
63 pwmbl_match(struct device *parent, void *match, void *aux)
64 {
65 	struct fdt_attach_args *faa = aux;
66 
67 	return OF_is_compatible(faa->fa_node, "pwm-backlight");
68 }
69 
70 void
71 pwmbl_attach(struct device *parent, struct device *self, void *aux)
72 {
73 	struct pwmbl_softc *sc = (struct pwmbl_softc *)self;
74 	struct fdt_attach_args *faa = aux;
75 	uint32_t *gpios;
76 	int i, len;
77 
78 	len = OF_getproplen(faa->fa_node, "pwms");
79 	if (len < 0) {
80 		printf(": no pwm\n");
81 		return;
82 	}
83 
84 	sc->sc_pwm = malloc(len, M_DEVBUF, M_WAITOK);
85 	OF_getpropintarray(faa->fa_node, "pwms", sc->sc_pwm, len);
86 	sc->sc_pwm_len = len;
87 
88 	len = OF_getproplen(faa->fa_node, "enable-gpios");
89 	if (len > 0) {
90 		gpios = malloc(len, M_TEMP, M_WAITOK);
91 		OF_getpropintarray(faa->fa_node, "enable-gpios", gpios, len);
92 		gpio_controller_config_pin(&gpios[0], GPIO_CONFIG_OUTPUT);
93 		gpio_controller_set_pin(&gpios[0], 1);
94 		free(gpios, M_TEMP, len);
95 	}
96 
97 	len = OF_getproplen(faa->fa_node, "brightness-levels");
98 	if (len > 0) {
99 		sc->sc_levels = malloc(len, M_DEVBUF, M_WAITOK);
100 		OF_getpropintarray(faa->fa_node, "brightness-levels",
101 		    sc->sc_levels, len);
102 		sc->sc_nlevels = len / sizeof(uint32_t);
103 		sc->sc_max_level = sc->sc_levels[sc->sc_nlevels - 1];
104 		sc->sc_def_level = OF_getpropint(faa->fa_node,
105 		    "default-brightness-level", sc->sc_nlevels - 1);
106 		if (sc->sc_def_level >= sc->sc_nlevels)
107 			sc->sc_def_level = sc->sc_nlevels - 1;
108 		sc->sc_def_level = sc->sc_levels[sc->sc_def_level];
109 	} else {
110 		sc->sc_nlevels = 256;
111 		sc->sc_levels = mallocarray(sc->sc_nlevels,
112 		    sizeof(uint32_t), M_DEVBUF, M_WAITOK);
113 		for (i = 0; i < sc->sc_nlevels; i++)
114 			sc->sc_levels[i] = i;
115 		sc->sc_max_level = sc->sc_levels[sc->sc_nlevels - 1];
116 		sc->sc_def_level = sc->sc_levels[sc->sc_nlevels - 1];
117 	}
118 
119 	printf("\n");
120 
121 	pwmbl_set_brightness(sc, sc->sc_def_level);
122 
123 	sc_pwmbl = sc;
124 	ws_get_param = pwmbl_get_param;
125 	ws_set_param = pwmbl_set_param;
126 }
127 
128 int
129 pwmbl_get_brightness(void *cookie, uint32_t *level)
130 {
131 	struct pwmbl_softc *sc = cookie;
132 	struct pwm_state ps;
133 
134 	if (pwm_get_state(sc->sc_pwm, &ps))
135 		return EINVAL;
136 
137 	*level = (ps.ps_pulse_width * sc->sc_max_level) / ps.ps_period;
138 	return 0;
139 }
140 
141 uint32_t
142 pwmbl_find_brightness(struct pwmbl_softc *sc, uint32_t level)
143 {
144 	uint32_t mid;
145 	int i;
146 
147 	for (i = 0; i < sc->sc_nlevels - 1; i++) {
148 		mid = (sc->sc_levels[i] + sc->sc_levels[i + 1]) / 2;
149 		if (sc->sc_levels[i] <= level && level <= mid)
150 			return sc->sc_levels[i];
151 		if (mid < level && level <= sc->sc_levels[i + 1])
152 			return sc->sc_levels[i + 1];
153 	}
154 	if (level < sc->sc_levels[0])
155 		return sc->sc_levels[0];
156 	else
157 		return sc->sc_levels[i];
158 }
159 
160 int
161 pwmbl_set_brightness(void *cookie, uint32_t level)
162 {
163 	struct pwmbl_softc *sc = cookie;
164 	struct pwm_state ps;
165 
166 	if (pwm_init_state(sc->sc_pwm, &ps))
167 		return EINVAL;
168 
169 	level = pwmbl_find_brightness(sc, level);
170 
171 	ps.ps_enabled = 1;
172 	ps.ps_pulse_width = (ps.ps_period * level) / sc->sc_max_level;
173 	return pwm_set_state(sc->sc_pwm, &ps);
174 }
175 
176 int
177 pwmbl_get_param(struct wsdisplay_param *dp)
178 {
179 	struct pwmbl_softc *sc = (struct pwmbl_softc *)sc_pwmbl;
180 	uint32_t level;
181 
182 	switch (dp->param) {
183 	case WSDISPLAYIO_PARAM_BRIGHTNESS:
184 		if (pwmbl_get_brightness(sc, &level))
185 			return -1;
186 
187 		dp->min = 0;
188 		dp->max = sc->sc_max_level;
189 		dp->curval = level;
190 		return 0;
191 	default:
192 		return -1;
193 	}
194 }
195 
196 int
197 pwmbl_set_param(struct wsdisplay_param *dp)
198 {
199 	struct pwmbl_softc *sc = (struct pwmbl_softc *)sc_pwmbl;
200 
201 	switch (dp->param) {
202 	case WSDISPLAYIO_PARAM_BRIGHTNESS:
203 		if (pwmbl_set_brightness(sc, dp->curval))
204 			return -1;
205 		return 0;
206 	default:
207 		return -1;
208 	}
209 }
210