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