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);
+}

Reply via email to