Module Name: src Committed By: jmcneill Date: Sun May 6 10:33:22 UTC 2018
Modified Files: src/sys/dev/fdt: fdtvar.h files.fdt Added Files: src/sys/dev/fdt: fdt_pwm.c pwm_backlight.c Log Message: Add support for PWM backlights. To generate a diff of this commit: cvs rdiff -u -r0 -r1.1 src/sys/dev/fdt/fdt_pwm.c \ src/sys/dev/fdt/pwm_backlight.c cvs rdiff -u -r1.29 -r1.30 src/sys/dev/fdt/fdtvar.h cvs rdiff -u -r1.24 -r1.25 src/sys/dev/fdt/files.fdt Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/fdt/fdtvar.h diff -u src/sys/dev/fdt/fdtvar.h:1.29 src/sys/dev/fdt/fdtvar.h:1.30 --- src/sys/dev/fdt/fdtvar.h:1.29 Sat Apr 7 18:05:08 2018 +++ src/sys/dev/fdt/fdtvar.h Sun May 6 10:33:21 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: fdtvar.h,v 1.29 2018/04/07 18:05:08 bouyer Exp $ */ +/* $NetBSD: fdtvar.h,v 1.30 2018/05/06 10:33:21 jmcneill Exp $ */ /*- * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> @@ -34,6 +34,7 @@ #include <sys/termios.h> #include <dev/i2c/i2cvar.h> +#include <dev/pwm/pwmvar.h> #include <dev/clk/clk.h> #include <dev/clock_subr.h> @@ -185,6 +186,10 @@ struct fdtbus_phy_controller_func { int (*enable)(device_t, void *, bool); }; +struct fdtbus_pwm_controller_func { + pwm_tag_t (*get_tag)(device_t, const void *, size_t); +}; + struct fdtbus_mmc_pwrseq; struct fdtbus_mmc_pwrseq_func { @@ -234,6 +239,8 @@ int fdtbus_register_power_controller(de const struct fdtbus_power_controller_func *); int fdtbus_register_phy_controller(device_t, int, const struct fdtbus_phy_controller_func *); +int fdtbus_register_pwm_controller(device_t, int, + const struct fdtbus_pwm_controller_func *); int fdtbus_register_mmc_pwrseq(device_t, int, const struct fdtbus_mmc_pwrseq_func *); @@ -257,6 +264,8 @@ int fdtbus_gpio_read(struct fdtbus_gpio void fdtbus_gpio_write(struct fdtbus_gpio_pin *, int); int fdtbus_gpio_read_raw(struct fdtbus_gpio_pin *); void fdtbus_gpio_write_raw(struct fdtbus_gpio_pin *, int); +pwm_tag_t fdtbus_pwm_acquire(int, const char *); +pwm_tag_t fdtbus_pwm_acquire_index(int, const char *, int); void fdtbus_pinctrl_configure(void); int fdtbus_pinctrl_set_config_index(int, u_int); int fdtbus_pinctrl_set_config(int, const char *); Index: src/sys/dev/fdt/files.fdt diff -u src/sys/dev/fdt/files.fdt:1.24 src/sys/dev/fdt/files.fdt:1.25 --- src/sys/dev/fdt/files.fdt:1.24 Tue May 1 23:59:15 2018 +++ src/sys/dev/fdt/files.fdt Sun May 6 10:33:21 2018 @@ -1,10 +1,10 @@ -# $NetBSD: files.fdt,v 1.24 2018/05/01 23:59:15 jmcneill Exp $ +# $NetBSD: files.fdt,v 1.25 2018/05/06 10:33:21 jmcneill Exp $ include "external/bsd/libfdt/conf/files.libfdt" defflag opt_fdt.h FDT: libfdt, ofw_subr -define fdtbus { } : clk +define fdtbus { } : clk, pwm device fdt { [pass = 10] } : fdtbus attach fdt at fdtbus @@ -54,6 +54,7 @@ file dev/fdt/fdt_intr.c fdtbus file dev/fdt/fdt_mmc_pwrseq.c fdtbus file dev/fdt/fdt_phy.c fdtbus file dev/fdt/fdt_power.c fdtbus +file dev/fdt/fdt_pwm.c fdtbus file dev/fdt/fdt_regulator.c fdtbus file dev/fdt/fdt_reset.c fdtbus file dev/fdt/fdt_rtc.c fdtbus @@ -67,6 +68,10 @@ device mmcpwrseq attach mmcpwrseq at fdt file dev/fdt/mmc_pwrseq_simple.c mmcpwrseq +device pwmbacklight +attach pwmbacklight at fdt +file dev/fdt/pwm_backlight.c pwmbacklight + define fdt_display_timing file dev/fdt/display_timing.c fdt_display_timing Added files: Index: src/sys/dev/fdt/fdt_pwm.c diff -u /dev/null src/sys/dev/fdt/fdt_pwm.c:1.1 --- /dev/null Sun May 6 10:33:22 2018 +++ src/sys/dev/fdt/fdt_pwm.c Sun May 6 10:33:21 2018 @@ -0,0 +1,116 @@ +/* $NetBSD: fdt_pwm.c,v 1.1 2018/05/06 10:33:21 jmcneill Exp $ */ + +/*- + * Copyright (c) 2018 Jared McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: fdt_pwm.c,v 1.1 2018/05/06 10:33:21 jmcneill Exp $"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/kmem.h> + +#include <libfdt.h> +#include <dev/fdt/fdtvar.h> + +struct fdtbus_pwm_controller { + device_t pc_dev; + int pc_phandle; + const struct fdtbus_pwm_controller_func *pc_funcs; + + struct fdtbus_pwm_controller *pc_next; +}; + +static struct fdtbus_pwm_controller *fdtbus_pc = NULL; + +int +fdtbus_register_pwm_controller(device_t dev, int phandle, + const struct fdtbus_pwm_controller_func *funcs) +{ + struct fdtbus_pwm_controller *pc; + + pc = kmem_alloc(sizeof(*pc), KM_SLEEP); + pc->pc_dev = dev; + pc->pc_phandle = phandle; + pc->pc_funcs = funcs; + + pc->pc_next = fdtbus_pc; + fdtbus_pc = pc; + + return 0; +} + +static struct fdtbus_pwm_controller * +fdtbus_get_pwm_controller(int phandle) +{ + struct fdtbus_pwm_controller *pc; + + for (pc = fdtbus_pc; pc; pc = pc->pc_next) { + if (pc->pc_phandle == phandle) { + return pc; + } + } + + return NULL; +} + +pwm_tag_t +fdtbus_pwm_acquire(int phandle, const char *prop) +{ + return fdtbus_pwm_acquire_index(phandle, prop, 0); +} + +pwm_tag_t +fdtbus_pwm_acquire_index(int phandle, const char *prop, int index) +{ + struct fdtbus_pwm_controller *pc; + const uint32_t *pwms, *p; + u_int n, pwm_cells; + int len, resid; + + pwms = fdtbus_get_prop(phandle, prop, &len); + if (pwms == NULL) + return NULL; + + p = pwms; + for (n = 0, resid = len; resid > 0; n++) { + const int pc_phandle = + fdtbus_get_phandle_from_native(be32toh(p[0])); + if (of_getprop_uint32(pc_phandle, "#pwm-cells", &pwm_cells)) + break; + if (n == index) { + pc = fdtbus_get_pwm_controller(pc_phandle); + if (pc == NULL) + return NULL; + return pc->pc_funcs->get_tag(pc->pc_dev, + &p[0], (pwm_cells + 1) * 4); + } + resid -= (pwm_cells + 1) * 4; + p += pwm_cells + 1; + } + + return NULL; +} Index: src/sys/dev/fdt/pwm_backlight.c diff -u /dev/null src/sys/dev/fdt/pwm_backlight.c:1.1 --- /dev/null Sun May 6 10:33:22 2018 +++ src/sys/dev/fdt/pwm_backlight.c Sun May 6 10:33:21 2018 @@ -0,0 +1,293 @@ +/* $NetBSD: pwm_backlight.c,v 1.1 2018/05/06 10:33:21 jmcneill Exp $ */ + +/*- + * Copyright (c) 2018 Jared McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: pwm_backlight.c,v 1.1 2018/05/06 10:33:21 jmcneill Exp $"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/device.h> +#include <sys/systm.h> +#include <sys/sysctl.h> +#include <sys/kmem.h> +#include <sys/gpio.h> + +#include <dev/pwm/pwmvar.h> + +#include <dev/fdt/fdtvar.h> + +struct pwm_backlight_softc { + device_t sc_dev; + pwm_tag_t sc_pwm; + struct fdtbus_gpio_pin *sc_pin; + + u_int *sc_levels; + u_int sc_nlevels; + + char *sc_levelstr; +}; + +static int pwm_backlight_match(device_t, cfdata_t, void *); +static void pwm_backlight_attach(device_t, device_t, void *); + +static void pwm_backlight_sysctl_init(struct pwm_backlight_softc *); +static void pwm_backlight_pmf_init(struct pwm_backlight_softc *); +static void pwm_backlight_set(struct pwm_backlight_softc *, u_int); +static u_int pwm_backlight_get(struct pwm_backlight_softc *); + +static const char *compatible[] = { + "pwm-backlight", + NULL +}; + +CFATTACH_DECL_NEW(pwmbacklight, sizeof(struct pwm_backlight_softc), + pwm_backlight_match, pwm_backlight_attach, NULL, NULL); + +static int +pwm_backlight_match(device_t parent, cfdata_t cf, void *aux) +{ + struct fdt_attach_args * const faa = aux; + + return of_match_compatible(faa->faa_phandle, compatible); +} + +static void +pwm_backlight_attach(device_t parent, device_t self, void *aux) +{ + struct pwm_backlight_softc * const sc = device_private(self); + struct fdt_attach_args * const faa = aux; + const int phandle = faa->faa_phandle; + const u_int *levels; + u_int default_level; + u_int n; + int len; + + sc->sc_dev = self; + sc->sc_pwm = fdtbus_pwm_acquire(phandle, "pwms"); + if (sc->sc_pwm == NULL) { + aprint_error(": couldn't acquire pwm\n"); + return; + } + if (of_hasprop(phandle, "enable-gpios")) { + sc->sc_pin = fdtbus_gpio_acquire(phandle, "enable-gpios", + GPIO_PIN_OUTPUT); + if (!sc->sc_pin) { + aprint_error(": couldn't acquire enable gpio\n"); + return; + } + } + + levels = fdtbus_get_prop(phandle, "brightness-levels", &len); + if (len < 4) { + aprint_error(": couldn't get 'brightness-levels' property\n"); + return; + } + sc->sc_levels = kmem_alloc(len, KM_SLEEP); + sc->sc_nlevels = len / 4; + for (n = 0; n < sc->sc_nlevels; n++) + sc->sc_levels[n] = be32toh(levels[n]); + + aprint_naive("\n"); + aprint_normal(": PWM Backlight"); + aprint_verbose(" <"); + for (n = 0; n < sc->sc_nlevels; n++) { + aprint_verbose("%s%u", n ? " " : "", sc->sc_levels[n]); + } + aprint_verbose(">"); + aprint_normal("\n"); + + if (of_getprop_uint32(phandle, "default-brightness-level", &default_level) == 0) { + /* set the default level now */ + pwm_backlight_set(sc, default_level); + } + + pwm_backlight_sysctl_init(sc); + pwm_backlight_pmf_init(sc); +} + +static void +pwm_backlight_set(struct pwm_backlight_softc *sc, u_int index) +{ + struct pwm_config conf; + + if (index >= sc->sc_nlevels) + return; + + aprint_debug_dev(sc->sc_dev, "set duty cycle to %u%%\n", sc->sc_levels[index]); + + pwm_disable(sc->sc_pwm); + pwm_get_config(sc->sc_pwm, &conf); + conf.duty_cycle = (conf.period * sc->sc_levels[index]) / sc->sc_levels[sc->sc_nlevels - 1]; + pwm_set_config(sc->sc_pwm, &conf); + pwm_enable(sc->sc_pwm); +} + +static u_int +pwm_backlight_get(struct pwm_backlight_softc *sc) +{ + struct pwm_config conf; + u_int raw_val, n; + + pwm_get_config(sc->sc_pwm, &conf); + + raw_val = (conf.duty_cycle * sc->sc_levels[sc->sc_nlevels - 1]) / conf.period; + + /* Return the closest setting to the raw value */ + for (n = 0; n < sc->sc_nlevels; n++) { + if (raw_val <= sc->sc_levels[n]) + break; + } + return n; +} + +static int +pwm_backlight_sysctl_helper(SYSCTLFN_ARGS) +{ + struct pwm_backlight_softc * const sc = rnode->sysctl_data; + struct sysctlnode node; + int error; + u_int level, n; + + node = *rnode; + node.sysctl_data = &level; + + n = pwm_backlight_get(sc); + level = sc->sc_levels[n]; + + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + for (n = 0; n < sc->sc_nlevels; n++) { + if (sc->sc_levels[n] == level) { + pwm_backlight_set(sc, n); + return 0; + } + } + + return EINVAL; +} + +static void +pwm_backlight_sysctl_init(struct pwm_backlight_softc *sc) +{ + const struct sysctlnode *node, *pwmnode; + struct sysctllog *log = NULL; + int error; + u_int n; + + sc->sc_levelstr = kmem_zalloc(strlen("XXXXX ") * sc->sc_nlevels, KM_SLEEP); + for (n = 0; n < sc->sc_nlevels; n++) { + char buf[7]; + snprintf(buf, sizeof(buf), n ? " %u" : "%u", sc->sc_levels[n]); + strcat(sc->sc_levelstr, buf); + } + + error = sysctl_createv(&log, 0, NULL, &node, + CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", NULL, + NULL, 0, NULL, 0, CTL_HW, CTL_EOL); + if (error) + goto failed; + + error = sysctl_createv(&log, 0, &node, &pwmnode, + 0, CTLTYPE_NODE, device_xname(sc->sc_dev), NULL, + NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); + if (error) + goto failed; + + error = sysctl_createv(&log, 0, &pwmnode, NULL, + 0, CTLTYPE_STRING, "levels", NULL, + NULL, 0, sc->sc_levelstr, NULL, + CTL_CREATE, CTL_EOL); + if (error) + goto failed; + + error = sysctl_createv(&log, 0, &pwmnode, NULL, + CTLFLAG_READWRITE, CTLTYPE_INT, "level", NULL, + pwm_backlight_sysctl_helper, 0, (void *)sc, 0, + CTL_CREATE, CTL_EOL); + if (error) + goto failed; + + return; + +failed: + aprint_error_dev(sc->sc_dev, "couldn't create sysctl nodes: %d\n", error); + sysctl_teardown(&log); +} + +static void +pwm_backlight_display_on(device_t dev) +{ + struct pwm_backlight_softc * const sc = device_private(dev); + + pwm_enable(sc->sc_pwm); +} + +static void +pwm_backlight_display_off(device_t dev) +{ + struct pwm_backlight_softc * const sc = device_private(dev); + + pwm_disable(sc->sc_pwm); +} + +static void +pwm_backlight_display_brightness_up(device_t dev) +{ + struct pwm_backlight_softc * const sc = device_private(dev); + u_int n; + + n = pwm_backlight_get(sc); + if (n < sc->sc_nlevels - 1) + pwm_backlight_set(sc, n + 1); +} + +static void +pwm_backlight_display_brightness_down(device_t dev) +{ + struct pwm_backlight_softc * const sc = device_private(dev); + u_int n; + + n = pwm_backlight_get(sc); + if (n > 0) + pwm_backlight_set(sc, n - 1); +} + +static void +pwm_backlight_pmf_init(struct pwm_backlight_softc *sc) +{ + pmf_event_register(sc->sc_dev, PMFE_DISPLAY_ON, + pwm_backlight_display_on, true); + pmf_event_register(sc->sc_dev, PMFE_DISPLAY_OFF, + pwm_backlight_display_off, true); + pmf_event_register(sc->sc_dev, PMFE_DISPLAY_BRIGHTNESS_UP, + pwm_backlight_display_brightness_up, true); + pmf_event_register(sc->sc_dev, PMFE_DISPLAY_BRIGHTNESS_DOWN, + pwm_backlight_display_brightness_down, true); +}