xref: /openbsd-src/sys/dev/fdt/pwmleds.c (revision 8550894424f8a4aa4aafb6cd57229dd6ed7cd9dd)
1 /*	$OpenBSD: pwmleds.c,v 1.1 2022/11/23 23:43:08 kettenis 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 };
43 
44 int	pwmleds_match(struct device *, void *, void *);
45 void	pwmleds_attach(struct device *, struct device *, void *);
46 
47 const struct cfattach pwmleds_ca = {
48 	sizeof (struct pwmleds_softc), pwmleds_match, pwmleds_attach
49 };
50 
51 struct cfdriver pwmleds_cd = {
52 	NULL, "pwmleds", DV_DULL
53 };
54 
55 int pwmleds_get_kbd_backlight(struct wskbd_backlight *);
56 int pwmleds_set_kbd_backlight(struct wskbd_backlight *);
57 
58 int
59 pwmleds_match(struct device *parent, void *match, void *aux)
60 {
61 	const struct fdt_attach_args	*faa = aux;
62 
63 	return OF_is_compatible(faa->fa_node, "pwm-leds");
64 }
65 
66 void
67 pwmleds_attach(struct device *parent, struct device *self, void *aux)
68 {
69 	struct pwmleds_softc *sc = (struct pwmleds_softc *)self;
70 	struct fdt_attach_args	*faa = aux;
71 	char *function;
72 	int len, node;
73 
74 	printf("\n");
75 
76 	for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) {
77 		len = OF_getproplen(node, "function");
78 		if (len <= 0)
79 			continue;
80 
81 		function = malloc(len, M_TEMP, M_WAITOK);
82 		OF_getprop(node, "function", function, len);
83 		if (strcmp(function, "kbd_backlight") != 0) {
84 			free(function, M_TEMP, len);
85 			continue;
86 		}
87 		free(function, M_TEMP, len);
88 
89 		len = OF_getproplen(node, "pwms");
90 		if (len <= 0)
91 			continue;
92 
93 		sc->sc_pwm = malloc(len, M_DEVBUF, M_WAITOK);
94 		OF_getpropintarray(node, "pwms", sc->sc_pwm, len);
95 		sc->sc_pwm_len = len;
96 
97 		sc->sc_max_brightness =
98 		    OF_getpropint(node, "max-brightness", 0);
99 		wskbd_get_backlight = pwmleds_get_kbd_backlight;
100 		wskbd_set_backlight = pwmleds_set_kbd_backlight;
101 	}
102 }
103 
104 struct pwmleds_softc *
105 pwmleds_kbd_backlight(void)
106 {
107 	struct pwmleds_softc *sc;
108 	int i;
109 
110 	for (i = 0; i < pwmleds_cd.cd_ndevs; i++) {
111 		sc = pwmleds_cd.cd_devs[i];
112 		if (sc == NULL)
113 			continue;
114 		if (sc->sc_max_brightness > 0)
115 			return sc;
116 	}
117 
118 	return NULL;
119 }
120 
121 int
122 pwmleds_get_kbd_backlight(struct wskbd_backlight *kbl)
123 {
124 	struct pwmleds_softc *sc;
125 	struct pwm_state ps;
126 	int error;
127 
128 	sc = pwmleds_kbd_backlight();
129 	if (sc == NULL)
130 		return ENOTTY;
131 
132 	error = pwm_get_state(sc->sc_pwm, &ps);
133 	if (error)
134 		return error;
135 
136 	kbl->min = 0;
137 	kbl->max = sc->sc_max_brightness;
138 	kbl->curval = (ps.ps_enabled) ?
139 	    ((uint64_t)ps.ps_pulse_width * kbl->max) / ps.ps_period : 0;
140 	return 0;
141 }
142 
143 int
144 pwmleds_set_kbd_backlight(struct wskbd_backlight *kbl)
145 {
146 	struct pwmleds_softc *sc;
147 	struct pwm_state ps;
148 
149 	sc = pwmleds_kbd_backlight();
150 	if (sc == NULL)
151 		return ENOTTY;
152 
153 	if (kbl->curval < 0 || kbl->curval > sc->sc_max_brightness)
154 		return EINVAL;
155 
156 	pwm_init_state(sc->sc_pwm, &ps);
157 	ps.ps_pulse_width =
158 	    ((uint64_t)kbl->curval * ps.ps_period) / sc->sc_max_brightness;
159 	ps.ps_enabled = (ps.ps_pulse_width > 0);
160 	return pwm_set_state(sc->sc_pwm, &ps);
161 }
162