pwm_backlight.c revision 1.4.2.2 1 1.4.2.2 pgoyette /* $NetBSD: pwm_backlight.c,v 1.4.2.2 2018/05/21 04:36:05 pgoyette Exp $ */
2 1.4.2.2 pgoyette
3 1.4.2.2 pgoyette /*-
4 1.4.2.2 pgoyette * Copyright (c) 2018 Jared McNeill <jmcneill (at) invisible.ca>
5 1.4.2.2 pgoyette * All rights reserved.
6 1.4.2.2 pgoyette *
7 1.4.2.2 pgoyette * Redistribution and use in source and binary forms, with or without
8 1.4.2.2 pgoyette * modification, are permitted provided that the following conditions
9 1.4.2.2 pgoyette * are met:
10 1.4.2.2 pgoyette * 1. Redistributions of source code must retain the above copyright
11 1.4.2.2 pgoyette * notice, this list of conditions and the following disclaimer.
12 1.4.2.2 pgoyette * 2. Redistributions in binary form must reproduce the above copyright
13 1.4.2.2 pgoyette * notice, this list of conditions and the following disclaimer in the
14 1.4.2.2 pgoyette * documentation and/or other materials provided with the distribution.
15 1.4.2.2 pgoyette *
16 1.4.2.2 pgoyette * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 1.4.2.2 pgoyette * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 1.4.2.2 pgoyette * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 1.4.2.2 pgoyette * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 1.4.2.2 pgoyette * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 1.4.2.2 pgoyette * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 1.4.2.2 pgoyette * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 1.4.2.2 pgoyette * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 1.4.2.2 pgoyette * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 1.4.2.2 pgoyette * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 1.4.2.2 pgoyette * SUCH DAMAGE.
27 1.4.2.2 pgoyette */
28 1.4.2.2 pgoyette
29 1.4.2.2 pgoyette #include <sys/cdefs.h>
30 1.4.2.2 pgoyette __KERNEL_RCSID(0, "$NetBSD: pwm_backlight.c,v 1.4.2.2 2018/05/21 04:36:05 pgoyette Exp $");
31 1.4.2.2 pgoyette
32 1.4.2.2 pgoyette #include <sys/param.h>
33 1.4.2.2 pgoyette #include <sys/bus.h>
34 1.4.2.2 pgoyette #include <sys/device.h>
35 1.4.2.2 pgoyette #include <sys/systm.h>
36 1.4.2.2 pgoyette #include <sys/sysctl.h>
37 1.4.2.2 pgoyette #include <sys/kmem.h>
38 1.4.2.2 pgoyette #include <sys/gpio.h>
39 1.4.2.2 pgoyette
40 1.4.2.2 pgoyette #include <dev/pwm/pwmvar.h>
41 1.4.2.2 pgoyette
42 1.4.2.2 pgoyette #include <dev/fdt/fdtvar.h>
43 1.4.2.2 pgoyette
44 1.4.2.2 pgoyette struct pwm_backlight_softc {
45 1.4.2.2 pgoyette device_t sc_dev;
46 1.4.2.2 pgoyette pwm_tag_t sc_pwm;
47 1.4.2.2 pgoyette struct fdtbus_gpio_pin *sc_pin;
48 1.4.2.2 pgoyette
49 1.4.2.2 pgoyette u_int *sc_levels;
50 1.4.2.2 pgoyette u_int sc_nlevels;
51 1.4.2.2 pgoyette
52 1.4.2.2 pgoyette char *sc_levelstr;
53 1.4.2.2 pgoyette
54 1.4.2.2 pgoyette bool sc_lid_state;
55 1.4.2.2 pgoyette };
56 1.4.2.2 pgoyette
57 1.4.2.2 pgoyette static int pwm_backlight_match(device_t, cfdata_t, void *);
58 1.4.2.2 pgoyette static void pwm_backlight_attach(device_t, device_t, void *);
59 1.4.2.2 pgoyette
60 1.4.2.2 pgoyette static void pwm_backlight_sysctl_init(struct pwm_backlight_softc *);
61 1.4.2.2 pgoyette static void pwm_backlight_pmf_init(struct pwm_backlight_softc *);
62 1.4.2.2 pgoyette static void pwm_backlight_set(struct pwm_backlight_softc *, u_int);
63 1.4.2.2 pgoyette static u_int pwm_backlight_get(struct pwm_backlight_softc *);
64 1.4.2.2 pgoyette
65 1.4.2.2 pgoyette static const char *compatible[] = {
66 1.4.2.2 pgoyette "pwm-backlight",
67 1.4.2.2 pgoyette NULL
68 1.4.2.2 pgoyette };
69 1.4.2.2 pgoyette
70 1.4.2.2 pgoyette CFATTACH_DECL_NEW(pwmbacklight, sizeof(struct pwm_backlight_softc),
71 1.4.2.2 pgoyette pwm_backlight_match, pwm_backlight_attach, NULL, NULL);
72 1.4.2.2 pgoyette
73 1.4.2.2 pgoyette static int
74 1.4.2.2 pgoyette pwm_backlight_match(device_t parent, cfdata_t cf, void *aux)
75 1.4.2.2 pgoyette {
76 1.4.2.2 pgoyette struct fdt_attach_args * const faa = aux;
77 1.4.2.2 pgoyette
78 1.4.2.2 pgoyette return of_match_compatible(faa->faa_phandle, compatible);
79 1.4.2.2 pgoyette }
80 1.4.2.2 pgoyette
81 1.4.2.2 pgoyette static void
82 1.4.2.2 pgoyette pwm_backlight_attach(device_t parent, device_t self, void *aux)
83 1.4.2.2 pgoyette {
84 1.4.2.2 pgoyette struct pwm_backlight_softc * const sc = device_private(self);
85 1.4.2.2 pgoyette struct fdt_attach_args * const faa = aux;
86 1.4.2.2 pgoyette const int phandle = faa->faa_phandle;
87 1.4.2.2 pgoyette const u_int *levels;
88 1.4.2.2 pgoyette u_int default_level;
89 1.4.2.2 pgoyette u_int n;
90 1.4.2.2 pgoyette int len;
91 1.4.2.2 pgoyette
92 1.4.2.2 pgoyette sc->sc_dev = self;
93 1.4.2.2 pgoyette sc->sc_pwm = fdtbus_pwm_acquire(phandle, "pwms");
94 1.4.2.2 pgoyette if (sc->sc_pwm == NULL) {
95 1.4.2.2 pgoyette aprint_error(": couldn't acquire pwm\n");
96 1.4.2.2 pgoyette return;
97 1.4.2.2 pgoyette }
98 1.4.2.2 pgoyette if (of_hasprop(phandle, "enable-gpios")) {
99 1.4.2.2 pgoyette sc->sc_pin = fdtbus_gpio_acquire(phandle, "enable-gpios",
100 1.4.2.2 pgoyette GPIO_PIN_OUTPUT);
101 1.4.2.2 pgoyette if (!sc->sc_pin) {
102 1.4.2.2 pgoyette aprint_error(": couldn't acquire enable gpio\n");
103 1.4.2.2 pgoyette return;
104 1.4.2.2 pgoyette }
105 1.4.2.2 pgoyette fdtbus_gpio_write(sc->sc_pin, 1);
106 1.4.2.2 pgoyette }
107 1.4.2.2 pgoyette
108 1.4.2.2 pgoyette levels = fdtbus_get_prop(phandle, "brightness-levels", &len);
109 1.4.2.2 pgoyette if (len < 4) {
110 1.4.2.2 pgoyette aprint_error(": couldn't get 'brightness-levels' property\n");
111 1.4.2.2 pgoyette return;
112 1.4.2.2 pgoyette }
113 1.4.2.2 pgoyette sc->sc_levels = kmem_alloc(len, KM_SLEEP);
114 1.4.2.2 pgoyette sc->sc_nlevels = len / 4;
115 1.4.2.2 pgoyette for (n = 0; n < sc->sc_nlevels; n++)
116 1.4.2.2 pgoyette sc->sc_levels[n] = be32toh(levels[n]);
117 1.4.2.2 pgoyette
118 1.4.2.2 pgoyette aprint_naive("\n");
119 1.4.2.2 pgoyette aprint_normal(": PWM Backlight");
120 1.4.2.2 pgoyette aprint_verbose(" <");
121 1.4.2.2 pgoyette for (n = 0; n < sc->sc_nlevels; n++) {
122 1.4.2.2 pgoyette aprint_verbose("%s%u", n ? " " : "", sc->sc_levels[n]);
123 1.4.2.2 pgoyette }
124 1.4.2.2 pgoyette aprint_verbose(">");
125 1.4.2.2 pgoyette aprint_normal("\n");
126 1.4.2.2 pgoyette
127 1.4.2.2 pgoyette sc->sc_lid_state = true;
128 1.4.2.2 pgoyette
129 1.4.2.2 pgoyette if (of_getprop_uint32(phandle, "default-brightness-level", &default_level) == 0) {
130 1.4.2.2 pgoyette /* set the default level now */
131 1.4.2.2 pgoyette pwm_backlight_set(sc, default_level);
132 1.4.2.2 pgoyette }
133 1.4.2.2 pgoyette
134 1.4.2.2 pgoyette pwm_backlight_sysctl_init(sc);
135 1.4.2.2 pgoyette pwm_backlight_pmf_init(sc);
136 1.4.2.2 pgoyette }
137 1.4.2.2 pgoyette
138 1.4.2.2 pgoyette static void
139 1.4.2.2 pgoyette pwm_backlight_set(struct pwm_backlight_softc *sc, u_int index)
140 1.4.2.2 pgoyette {
141 1.4.2.2 pgoyette struct pwm_config conf;
142 1.4.2.2 pgoyette
143 1.4.2.2 pgoyette if (index >= sc->sc_nlevels)
144 1.4.2.2 pgoyette return;
145 1.4.2.2 pgoyette
146 1.4.2.2 pgoyette aprint_debug_dev(sc->sc_dev, "set duty cycle to %u%%\n", sc->sc_levels[index]);
147 1.4.2.2 pgoyette
148 1.4.2.2 pgoyette pwm_disable(sc->sc_pwm);
149 1.4.2.2 pgoyette pwm_get_config(sc->sc_pwm, &conf);
150 1.4.2.2 pgoyette conf.duty_cycle = (conf.period * sc->sc_levels[index]) / sc->sc_levels[sc->sc_nlevels - 1];
151 1.4.2.2 pgoyette pwm_set_config(sc->sc_pwm, &conf);
152 1.4.2.2 pgoyette pwm_enable(sc->sc_pwm);
153 1.4.2.2 pgoyette }
154 1.4.2.2 pgoyette
155 1.4.2.2 pgoyette static u_int
156 1.4.2.2 pgoyette pwm_backlight_get(struct pwm_backlight_softc *sc)
157 1.4.2.2 pgoyette {
158 1.4.2.2 pgoyette struct pwm_config conf;
159 1.4.2.2 pgoyette u_int raw_val, n;
160 1.4.2.2 pgoyette
161 1.4.2.2 pgoyette pwm_get_config(sc->sc_pwm, &conf);
162 1.4.2.2 pgoyette
163 1.4.2.2 pgoyette raw_val = (conf.duty_cycle * sc->sc_levels[sc->sc_nlevels - 1]) / conf.period;
164 1.4.2.2 pgoyette
165 1.4.2.2 pgoyette /* Return the closest setting to the raw value */
166 1.4.2.2 pgoyette for (n = 0; n < sc->sc_nlevels; n++) {
167 1.4.2.2 pgoyette if (raw_val <= sc->sc_levels[n])
168 1.4.2.2 pgoyette break;
169 1.4.2.2 pgoyette }
170 1.4.2.2 pgoyette return n;
171 1.4.2.2 pgoyette }
172 1.4.2.2 pgoyette
173 1.4.2.2 pgoyette static int
174 1.4.2.2 pgoyette pwm_backlight_sysctl_helper(SYSCTLFN_ARGS)
175 1.4.2.2 pgoyette {
176 1.4.2.2 pgoyette struct pwm_backlight_softc * const sc = rnode->sysctl_data;
177 1.4.2.2 pgoyette struct sysctlnode node;
178 1.4.2.2 pgoyette int error;
179 1.4.2.2 pgoyette u_int level, n;
180 1.4.2.2 pgoyette
181 1.4.2.2 pgoyette node = *rnode;
182 1.4.2.2 pgoyette node.sysctl_data = &level;
183 1.4.2.2 pgoyette
184 1.4.2.2 pgoyette n = pwm_backlight_get(sc);
185 1.4.2.2 pgoyette level = sc->sc_levels[n];
186 1.4.2.2 pgoyette
187 1.4.2.2 pgoyette error = sysctl_lookup(SYSCTLFN_CALL(&node));
188 1.4.2.2 pgoyette if (error || newp == NULL)
189 1.4.2.2 pgoyette return error;
190 1.4.2.2 pgoyette
191 1.4.2.2 pgoyette for (n = 0; n < sc->sc_nlevels; n++) {
192 1.4.2.2 pgoyette if (sc->sc_levels[n] == level) {
193 1.4.2.2 pgoyette pwm_backlight_set(sc, n);
194 1.4.2.2 pgoyette return 0;
195 1.4.2.2 pgoyette }
196 1.4.2.2 pgoyette }
197 1.4.2.2 pgoyette
198 1.4.2.2 pgoyette return EINVAL;
199 1.4.2.2 pgoyette }
200 1.4.2.2 pgoyette
201 1.4.2.2 pgoyette static void
202 1.4.2.2 pgoyette pwm_backlight_sysctl_init(struct pwm_backlight_softc *sc)
203 1.4.2.2 pgoyette {
204 1.4.2.2 pgoyette const struct sysctlnode *node, *pwmnode;
205 1.4.2.2 pgoyette struct sysctllog *log = NULL;
206 1.4.2.2 pgoyette int error;
207 1.4.2.2 pgoyette u_int n;
208 1.4.2.2 pgoyette
209 1.4.2.2 pgoyette sc->sc_levelstr = kmem_zalloc(strlen("XXXXX ") * sc->sc_nlevels, KM_SLEEP);
210 1.4.2.2 pgoyette for (n = 0; n < sc->sc_nlevels; n++) {
211 1.4.2.2 pgoyette char buf[7];
212 1.4.2.2 pgoyette snprintf(buf, sizeof(buf), n ? " %u" : "%u", sc->sc_levels[n]);
213 1.4.2.2 pgoyette strcat(sc->sc_levelstr, buf);
214 1.4.2.2 pgoyette }
215 1.4.2.2 pgoyette
216 1.4.2.2 pgoyette error = sysctl_createv(&log, 0, NULL, &node,
217 1.4.2.2 pgoyette CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", NULL,
218 1.4.2.2 pgoyette NULL, 0, NULL, 0, CTL_HW, CTL_EOL);
219 1.4.2.2 pgoyette if (error)
220 1.4.2.2 pgoyette goto failed;
221 1.4.2.2 pgoyette
222 1.4.2.2 pgoyette error = sysctl_createv(&log, 0, &node, &pwmnode,
223 1.4.2.2 pgoyette 0, CTLTYPE_NODE, device_xname(sc->sc_dev), NULL,
224 1.4.2.2 pgoyette NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
225 1.4.2.2 pgoyette if (error)
226 1.4.2.2 pgoyette goto failed;
227 1.4.2.2 pgoyette
228 1.4.2.2 pgoyette error = sysctl_createv(&log, 0, &pwmnode, NULL,
229 1.4.2.2 pgoyette 0, CTLTYPE_STRING, "levels", NULL,
230 1.4.2.2 pgoyette NULL, 0, sc->sc_levelstr, 0,
231 1.4.2.2 pgoyette CTL_CREATE, CTL_EOL);
232 1.4.2.2 pgoyette if (error)
233 1.4.2.2 pgoyette goto failed;
234 1.4.2.2 pgoyette
235 1.4.2.2 pgoyette error = sysctl_createv(&log, 0, &pwmnode, NULL,
236 1.4.2.2 pgoyette CTLFLAG_READWRITE, CTLTYPE_INT, "level", NULL,
237 1.4.2.2 pgoyette pwm_backlight_sysctl_helper, 0, (void *)sc, 0,
238 1.4.2.2 pgoyette CTL_CREATE, CTL_EOL);
239 1.4.2.2 pgoyette if (error)
240 1.4.2.2 pgoyette goto failed;
241 1.4.2.2 pgoyette
242 1.4.2.2 pgoyette return;
243 1.4.2.2 pgoyette
244 1.4.2.2 pgoyette failed:
245 1.4.2.2 pgoyette aprint_error_dev(sc->sc_dev, "couldn't create sysctl nodes: %d\n", error);
246 1.4.2.2 pgoyette sysctl_teardown(&log);
247 1.4.2.2 pgoyette }
248 1.4.2.2 pgoyette
249 1.4.2.2 pgoyette static void
250 1.4.2.2 pgoyette pwm_backlight_display_on(device_t dev)
251 1.4.2.2 pgoyette {
252 1.4.2.2 pgoyette struct pwm_backlight_softc * const sc = device_private(dev);
253 1.4.2.2 pgoyette
254 1.4.2.2 pgoyette if (sc->sc_pin && sc->sc_lid_state)
255 1.4.2.2 pgoyette fdtbus_gpio_write(sc->sc_pin, 1);
256 1.4.2.2 pgoyette }
257 1.4.2.2 pgoyette
258 1.4.2.2 pgoyette static void
259 1.4.2.2 pgoyette pwm_backlight_display_off(device_t dev)
260 1.4.2.2 pgoyette {
261 1.4.2.2 pgoyette struct pwm_backlight_softc * const sc = device_private(dev);
262 1.4.2.2 pgoyette
263 1.4.2.2 pgoyette if (sc->sc_pin)
264 1.4.2.2 pgoyette fdtbus_gpio_write(sc->sc_pin, 0);
265 1.4.2.2 pgoyette }
266 1.4.2.2 pgoyette
267 1.4.2.2 pgoyette static void
268 1.4.2.2 pgoyette pwm_backlight_chassis_lid_open(device_t dev)
269 1.4.2.2 pgoyette {
270 1.4.2.2 pgoyette struct pwm_backlight_softc * const sc = device_private(dev);
271 1.4.2.2 pgoyette
272 1.4.2.2 pgoyette sc->sc_lid_state = true;
273 1.4.2.2 pgoyette
274 1.4.2.2 pgoyette if (sc->sc_pin)
275 1.4.2.2 pgoyette fdtbus_gpio_write(sc->sc_pin, 1);
276 1.4.2.2 pgoyette }
277 1.4.2.2 pgoyette
278 1.4.2.2 pgoyette static void
279 1.4.2.2 pgoyette pwm_backlight_chassis_lid_close(device_t dev)
280 1.4.2.2 pgoyette {
281 1.4.2.2 pgoyette struct pwm_backlight_softc * const sc = device_private(dev);
282 1.4.2.2 pgoyette
283 1.4.2.2 pgoyette sc->sc_lid_state = false;
284 1.4.2.2 pgoyette
285 1.4.2.2 pgoyette if (sc->sc_pin)
286 1.4.2.2 pgoyette fdtbus_gpio_write(sc->sc_pin, 0);
287 1.4.2.2 pgoyette }
288 1.4.2.2 pgoyette
289 1.4.2.2 pgoyette static void
290 1.4.2.2 pgoyette pwm_backlight_display_brightness_up(device_t dev)
291 1.4.2.2 pgoyette {
292 1.4.2.2 pgoyette struct pwm_backlight_softc * const sc = device_private(dev);
293 1.4.2.2 pgoyette u_int n;
294 1.4.2.2 pgoyette
295 1.4.2.2 pgoyette n = pwm_backlight_get(sc);
296 1.4.2.2 pgoyette if (n < sc->sc_nlevels - 1)
297 1.4.2.2 pgoyette pwm_backlight_set(sc, n + 1);
298 1.4.2.2 pgoyette }
299 1.4.2.2 pgoyette
300 1.4.2.2 pgoyette static void
301 1.4.2.2 pgoyette pwm_backlight_display_brightness_down(device_t dev)
302 1.4.2.2 pgoyette {
303 1.4.2.2 pgoyette struct pwm_backlight_softc * const sc = device_private(dev);
304 1.4.2.2 pgoyette u_int n;
305 1.4.2.2 pgoyette
306 1.4.2.2 pgoyette n = pwm_backlight_get(sc);
307 1.4.2.2 pgoyette if (n > 0)
308 1.4.2.2 pgoyette pwm_backlight_set(sc, n - 1);
309 1.4.2.2 pgoyette }
310 1.4.2.2 pgoyette
311 1.4.2.2 pgoyette static void
312 1.4.2.2 pgoyette pwm_backlight_pmf_init(struct pwm_backlight_softc *sc)
313 1.4.2.2 pgoyette {
314 1.4.2.2 pgoyette pmf_event_register(sc->sc_dev, PMFE_DISPLAY_ON,
315 1.4.2.2 pgoyette pwm_backlight_display_on, true);
316 1.4.2.2 pgoyette pmf_event_register(sc->sc_dev, PMFE_DISPLAY_OFF,
317 1.4.2.2 pgoyette pwm_backlight_display_off, true);
318 1.4.2.2 pgoyette pmf_event_register(sc->sc_dev, PMFE_CHASSIS_LID_OPEN,
319 1.4.2.2 pgoyette pwm_backlight_chassis_lid_open, true);
320 1.4.2.2 pgoyette pmf_event_register(sc->sc_dev, PMFE_CHASSIS_LID_CLOSE,
321 1.4.2.2 pgoyette pwm_backlight_chassis_lid_close, true);
322 1.4.2.2 pgoyette pmf_event_register(sc->sc_dev, PMFE_DISPLAY_BRIGHTNESS_UP,
323 1.4.2.2 pgoyette pwm_backlight_display_brightness_up, true);
324 1.4.2.2 pgoyette pmf_event_register(sc->sc_dev, PMFE_DISPLAY_BRIGHTNESS_DOWN,
325 1.4.2.2 pgoyette pwm_backlight_display_brightness_down, true);
326 1.4.2.2 pgoyette }
327