Add support for PWM backlights.
This commit is contained in:
parent
2a22daa1c9
commit
39f9e242ac
116
sys/dev/fdt/fdt_pwm.c
Normal file
116
sys/dev/fdt/fdt_pwm.c
Normal file
@ -0,0 +1,116 @@
|
||||
/* $NetBSD: fdt_pwm.c,v 1.1 2018/05/06 10:33:21 jmcneill Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2018 Jared McNeill <jmcneill@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;
|
||||
}
|
@ -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 <jmcneill@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(device_t, int,
|
||||
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_pin *);
|
||||
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 *);
|
||||
|
@ -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
|
||||
|
||||
|
293
sys/dev/fdt/pwm_backlight.c
Normal file
293
sys/dev/fdt/pwm_backlight.c
Normal file
@ -0,0 +1,293 @@
|
||||
/* $NetBSD: pwm_backlight.c,v 1.1 2018/05/06 10:33:21 jmcneill Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2018 Jared McNeill <jmcneill@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);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user