xref: /openbsd-src/sys/dev/fdt/pwmleds.c (revision 893072db7b2e7ca36afd1649d87fe3054ac0eaaa)
1 /*	$OpenBSD: pwmleds.c,v 1.2 2023/04/25 11:12:38 tobhe Exp $	*/
2 /*
3  * Copyright (c) 2022 Mark Kettenis <kettenis@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21 #include <sys/malloc.h>
22 
23 #include <machine/bus.h>
24 #include <machine/fdt.h>
25 
26 #include <dev/ofw/openfirm.h>
27 #include <dev/ofw/ofw_misc.h>
28 #include <dev/ofw/fdt.h>
29 
30 #include <dev/wscons/wsconsio.h>
31 
32 extern int (*wskbd_get_backlight)(struct wskbd_backlight *);
33 extern int (*wskbd_set_backlight)(struct wskbd_backlight *);
34 
35 struct pwmleds_softc {
36 	struct device		sc_dev;
37 
38 	/* Keyboard backlight. */
39 	uint32_t		*sc_pwm;
40 	int			 sc_pwm_len;
41 	uint32_t		 sc_max_brightness;
42 	struct pwm_state	 sc_ps_saved;
43 };
44 
45 int	pwmleds_match(struct device *, void *, void *);
46 void	pwmleds_attach(struct device *, struct device *, void *);
47 int	pwmleds_activate(struct device *, int);
48 
49 const struct cfattach pwmleds_ca = {
50 	sizeof (struct pwmleds_softc), pwmleds_match, pwmleds_attach, NULL,
51 	pwmleds_activate
52 };
53 
54 struct cfdriver pwmleds_cd = {
55 	NULL, "pwmleds", DV_DULL
56 };
57 
58 int pwmleds_get_kbd_backlight(struct wskbd_backlight *);
59 int pwmleds_set_kbd_backlight(struct wskbd_backlight *);
60 
61 int
pwmleds_match(struct device * parent,void * match,void * aux)62 pwmleds_match(struct device *parent, void *match, void *aux)
63 {
64 	const struct fdt_attach_args	*faa = aux;
65 
66 	return OF_is_compatible(faa->fa_node, "pwm-leds");
67 }
68 
69 void
pwmleds_attach(struct device * parent,struct device * self,void * aux)70 pwmleds_attach(struct device *parent, struct device *self, void *aux)
71 {
72 	struct pwmleds_softc *sc = (struct pwmleds_softc *)self;
73 	struct fdt_attach_args	*faa = aux;
74 	char *function;
75 	int len, node;
76 
77 	printf("\n");
78 
79 	for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) {
80 		len = OF_getproplen(node, "function");
81 		if (len <= 0)
82 			continue;
83 
84 		function = malloc(len, M_TEMP, M_WAITOK);
85 		OF_getprop(node, "function", function, len);
86 		if (strcmp(function, "kbd_backlight") != 0) {
87 			free(function, M_TEMP, len);
88 			continue;
89 		}
90 		free(function, M_TEMP, len);
91 
92 		len = OF_getproplen(node, "pwms");
93 		if (len <= 0)
94 			continue;
95 
96 		sc->sc_pwm = malloc(len, M_DEVBUF, M_WAITOK);
97 		OF_getpropintarray(node, "pwms", sc->sc_pwm, len);
98 		sc->sc_pwm_len = len;
99 
100 		sc->sc_max_brightness =
101 		    OF_getpropint(node, "max-brightness", 0);
102 		wskbd_get_backlight = pwmleds_get_kbd_backlight;
103 		wskbd_set_backlight = pwmleds_set_kbd_backlight;
104 	}
105 }
106 
107 int
pwmleds_activate(struct device * self,int act)108 pwmleds_activate(struct device *self, int act)
109 {
110 	struct pwmleds_softc *sc = (struct pwmleds_softc *)self;
111 	struct pwm_state ps;
112 	int error;
113 
114 	switch (act) {
115 	case DVACT_QUIESCE:
116 		error = pwm_get_state(sc->sc_pwm, &sc->sc_ps_saved);
117 		if (error)
118 			return error;
119 
120 		pwm_init_state(sc->sc_pwm, &ps);
121 		ps.ps_pulse_width = 0;
122 		ps.ps_enabled = 0;
123 		return pwm_set_state(sc->sc_pwm, &ps);
124 	case DVACT_WAKEUP:
125 		return pwm_set_state(sc->sc_pwm, &sc->sc_ps_saved);
126 	}
127 	return 0;
128 }
129 
130 struct pwmleds_softc *
pwmleds_kbd_backlight(void)131 pwmleds_kbd_backlight(void)
132 {
133 	struct pwmleds_softc *sc;
134 	int i;
135 
136 	for (i = 0; i < pwmleds_cd.cd_ndevs; i++) {
137 		sc = pwmleds_cd.cd_devs[i];
138 		if (sc == NULL)
139 			continue;
140 		if (sc->sc_max_brightness > 0)
141 			return sc;
142 	}
143 
144 	return NULL;
145 }
146 
147 int
pwmleds_get_kbd_backlight(struct wskbd_backlight * kbl)148 pwmleds_get_kbd_backlight(struct wskbd_backlight *kbl)
149 {
150 	struct pwmleds_softc *sc;
151 	struct pwm_state ps;
152 	int error;
153 
154 	sc = pwmleds_kbd_backlight();
155 	if (sc == NULL)
156 		return ENOTTY;
157 
158 	error = pwm_get_state(sc->sc_pwm, &ps);
159 	if (error)
160 		return error;
161 
162 	kbl->min = 0;
163 	kbl->max = sc->sc_max_brightness;
164 	kbl->curval = (ps.ps_enabled) ?
165 	    ((uint64_t)ps.ps_pulse_width * kbl->max) / ps.ps_period : 0;
166 	return 0;
167 }
168 
169 int
pwmleds_set_kbd_backlight(struct wskbd_backlight * kbl)170 pwmleds_set_kbd_backlight(struct wskbd_backlight *kbl)
171 {
172 	struct pwmleds_softc *sc;
173 	struct pwm_state ps;
174 
175 	sc = pwmleds_kbd_backlight();
176 	if (sc == NULL)
177 		return ENOTTY;
178 
179 	if (kbl->curval < 0 || kbl->curval > sc->sc_max_brightness)
180 		return EINVAL;
181 
182 	pwm_init_state(sc->sc_pwm, &ps);
183 	ps.ps_pulse_width =
184 	    ((uint64_t)kbl->curval * ps.ps_period) / sc->sc_max_brightness;
185 	ps.ps_enabled = (ps.ps_pulse_width > 0);
186 	return pwm_set_state(sc->sc_pwm, &ps);
187 }
188