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