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