The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/pwm/pwm_backlight.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2020 Emmanuel Vadot <manu@FreeBSD.org>
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   25  * SUCH DAMAGE.
   26  *
   27  * $FreeBSD$
   28  */
   29 
   30 #include <sys/cdefs.h>
   31 __FBSDID("$FreeBSD$");
   32 
   33 
   34 #include <sys/param.h>
   35 #include <sys/systm.h>
   36 #include <sys/bio.h>
   37 #include <sys/bus.h>
   38 #include <sys/conf.h>
   39 #include <sys/endian.h>
   40 #include <sys/fcntl.h>
   41 #include <sys/ioccom.h>
   42 #include <sys/kernel.h>
   43 #include <sys/kthread.h>
   44 #include <sys/lock.h>
   45 #include <sys/malloc.h>
   46 #include <sys/module.h>
   47 #include <sys/mutex.h>
   48 #include <sys/priv.h>
   49 #include <sys/slicer.h>
   50 #include <sys/sysctl.h>
   51 #include <sys/time.h>
   52 
   53 #include <dev/ofw/ofw_bus.h>
   54 #include <dev/ofw/ofw_bus_subr.h>
   55 
   56 #include <dev/extres/regulator/regulator.h>
   57 
   58 #include <dev/backlight/backlight.h>
   59 
   60 #include <dev/pwm/ofw_pwm.h>
   61 
   62 #include "backlight_if.h"
   63 #include "pwmbus_if.h"
   64 
   65 struct pwm_backlight_softc {
   66         device_t        pwmdev;
   67         struct cdev     *cdev;
   68 
   69         pwm_channel_t   channel;
   70         uint32_t        *levels;
   71         ssize_t         nlevels;
   72         int             default_level;
   73         ssize_t         current_level;
   74 
   75         regulator_t     power_supply;
   76         uint64_t        period;
   77         uint64_t        duty;
   78         bool            enabled;
   79 };
   80 
   81 static int pwm_backlight_find_level_per_percent(struct pwm_backlight_softc *sc, int percent);
   82 
   83 static struct ofw_compat_data compat_data[] = {
   84         { "pwm-backlight",      1 },
   85         { NULL,                 0 }
   86 };
   87 
   88 static int
   89 pwm_backlight_probe(device_t dev)
   90 {
   91 
   92         if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
   93                 return (ENXIO);
   94 
   95         device_set_desc(dev, "PWM Backlight");
   96         return (BUS_PROBE_DEFAULT);
   97 }
   98 
   99 static int
  100 pwm_backlight_attach(device_t dev)
  101 {
  102         struct pwm_backlight_softc *sc;
  103         phandle_t node;
  104         int rv;
  105 
  106         sc = device_get_softc(dev);
  107         node = ofw_bus_get_node(dev);
  108 
  109         rv = pwm_get_by_ofw_propidx(dev, node, "pwms", 0, &sc->channel);
  110         if (rv != 0) {
  111                 device_printf(dev, "Cannot map pwm channel %d\n", rv);
  112                 return (ENXIO);
  113         }
  114 
  115         if (regulator_get_by_ofw_property(dev, 0, "power-supply",
  116             &sc->power_supply) != 0) {
  117                 device_printf(dev, "No power-supply property\n");
  118                 return (ENXIO);
  119         }
  120 
  121         if (OF_hasprop(node, "brightness-levels")) {
  122                 sc->nlevels = OF_getencprop_alloc(node, "brightness-levels",
  123                     (void **)&sc->levels);
  124                 if (sc->nlevels <= 0) {
  125                         device_printf(dev, "Cannot parse brightness levels\n");
  126                         return (ENXIO);
  127                 }
  128                 sc->nlevels /= sizeof(uint32_t);
  129 
  130                 if (OF_getencprop(node, "default-brightness-level",
  131                     &sc->default_level, sizeof(uint32_t)) <= 0) {
  132                         device_printf(dev, "No default-brightness-level while brightness-levels is specified\n");
  133                         return (ENXIO);
  134                 } else {
  135                         if (sc->default_level > sc->nlevels) {
  136                                 device_printf(dev, "default-brightness-level isn't present in brightness-levels range\n");
  137                                 return (ENXIO);
  138                         }
  139                         sc->channel->duty = sc->channel->period * sc->levels[sc->default_level] / 100;
  140                 }
  141 
  142                 if (bootverbose) {
  143                         device_printf(dev, "Number of levels: %zd\n", sc->nlevels);
  144                         device_printf(dev, "Configured period time: %ju\n", (uintmax_t)sc->channel->period);
  145                         device_printf(dev, "Default duty cycle: %ju\n", (uintmax_t)sc->channel->duty);
  146                 }
  147         } else {
  148                 /* Get the current backlight level */
  149                 PWMBUS_CHANNEL_GET_CONFIG(sc->channel->dev,
  150                     sc->channel->channel,
  151                     (unsigned int *)&sc->channel->period,
  152                     (unsigned int *)&sc->channel->duty);
  153                 if (sc->channel->duty > sc->channel->period)
  154                         sc->channel->duty = sc->channel->period;
  155                 if (bootverbose) {
  156                         device_printf(dev, "Configured period time: %ju\n", (uintmax_t)sc->channel->period);
  157                         device_printf(dev, "Default duty cycle: %ju\n", (uintmax_t)sc->channel->duty);
  158                 }
  159         }
  160 
  161         regulator_enable(sc->power_supply);
  162         sc->channel->enabled = true;
  163         PWMBUS_CHANNEL_CONFIG(sc->channel->dev, sc->channel->channel,
  164             sc->channel->period, sc->channel->duty);
  165         PWMBUS_CHANNEL_ENABLE(sc->channel->dev, sc->channel->channel,
  166             sc->channel->enabled);
  167 
  168         sc->current_level = pwm_backlight_find_level_per_percent(sc,
  169             sc->channel->period / sc->channel->duty);
  170         sc->cdev = backlight_register("pwm_backlight", dev);
  171         if (sc->cdev == NULL)
  172                 device_printf(dev, "Cannot register as a backlight\n");
  173 
  174         return (0);
  175 }
  176 
  177 static int
  178 pwm_backlight_detach(device_t dev)
  179 {
  180         struct pwm_backlight_softc *sc;
  181 
  182         sc = device_get_softc(dev);
  183         if (sc->nlevels > 0)
  184                 OF_prop_free(sc->levels);
  185         regulator_disable(sc->power_supply);
  186         backlight_destroy(sc->cdev);
  187         return (0);
  188 }
  189 
  190 static int
  191 pwm_backlight_find_level_per_percent(struct pwm_backlight_softc *sc, int percent)
  192 {
  193         int i;
  194         int diff;
  195 
  196         if (percent < 0 || percent > 100)
  197                 return (-1);
  198 
  199         for (i = 0, diff = 0; i < sc->nlevels; i++) {
  200                 if (sc->levels[i] == percent)
  201                         return (i);
  202                 else if (sc->levels[i] < percent)
  203                         diff = percent - sc->levels[i];
  204                 else {
  205                         if (diff < abs((percent - sc->levels[i])))
  206                                 return (i - 1);
  207                         else
  208                                 return (i);
  209                 }
  210         }
  211 
  212         return (-1);
  213 }
  214 
  215 static int
  216 pwm_backlight_update_status(device_t dev, struct backlight_props *props)
  217 {
  218         struct pwm_backlight_softc *sc;
  219         int reg_status;
  220         int error;
  221 
  222         sc = device_get_softc(dev);
  223 
  224         if (sc->nlevels != 0) {
  225                 error = pwm_backlight_find_level_per_percent(sc,
  226                     props->brightness);
  227                 if (error < 0)
  228                         return (ERANGE);
  229                 sc->current_level = error;
  230                 sc->channel->duty = sc->channel->period *
  231                         sc->levels[sc->current_level] / 100;
  232         } else {
  233                 if (props->brightness > 100 || props->brightness < 0)
  234                         return (ERANGE);
  235                 sc->channel->duty = sc->channel->period *
  236                         props->brightness / 100;
  237         }
  238         sc->channel->enabled = true;
  239         PWMBUS_CHANNEL_CONFIG(sc->channel->dev, sc->channel->channel,
  240             sc->channel->period, sc->channel->duty);
  241         PWMBUS_CHANNEL_ENABLE(sc->channel->dev, sc->channel->channel,
  242             sc->channel->enabled);
  243         error = regulator_status(sc->power_supply, &reg_status);
  244         if (error != 0)
  245                 device_printf(dev,
  246                     "Cannot get power-supply status: %d\n", error);
  247         else {
  248                 if (props->brightness > 0) {
  249                         if (reg_status != REGULATOR_STATUS_ENABLED)
  250                                 regulator_enable(sc->power_supply);
  251                 } else {
  252                         if (reg_status == REGULATOR_STATUS_ENABLED)
  253                                 regulator_disable(sc->power_supply);
  254                 }
  255         }
  256 
  257         return (0);
  258 }
  259 
  260 static int
  261 pwm_backlight_get_status(device_t dev, struct backlight_props *props)
  262 {
  263         struct pwm_backlight_softc *sc;
  264         int i;
  265 
  266         sc = device_get_softc(dev);
  267 
  268         if (sc->nlevels != 0) {
  269                 props->brightness = sc->levels[sc->current_level];
  270                 props->nlevels = sc->nlevels;
  271                 for (i = 0; i < sc->nlevels; i++)
  272                         props->levels[i] = sc->levels[i];
  273         } else {
  274                 props->brightness = sc->channel->duty * 100 / sc->channel->period;
  275                 props->nlevels = 0;
  276         }
  277         return (0);
  278 }
  279 
  280 static int
  281 pwm_backlight_get_info(device_t dev, struct backlight_info *info)
  282 {
  283 
  284         info->type = BACKLIGHT_TYPE_PANEL;
  285         strlcpy(info->name, "pwm-backlight", BACKLIGHTMAXNAMELENGTH);
  286         return (0);
  287 }
  288 
  289 static device_method_t pwm_backlight_methods[] = {
  290         /* device_if */
  291         DEVMETHOD(device_probe, pwm_backlight_probe),
  292         DEVMETHOD(device_attach, pwm_backlight_attach),
  293         DEVMETHOD(device_detach, pwm_backlight_detach),
  294 
  295         /* backlight interface */
  296         DEVMETHOD(backlight_update_status, pwm_backlight_update_status),
  297         DEVMETHOD(backlight_get_status, pwm_backlight_get_status),
  298         DEVMETHOD(backlight_get_info, pwm_backlight_get_info),
  299         DEVMETHOD_END
  300 };
  301 
  302 driver_t pwm_backlight_driver = {
  303         "pwm_backlight",
  304         pwm_backlight_methods,
  305         sizeof(struct pwm_backlight_softc),
  306 };
  307 
  308 DRIVER_MODULE(pwm_backlight, simplebus, pwm_backlight_driver, 0, 0);
  309 MODULE_DEPEND(pwm_backlight, backlight, 1, 1, 1);
  310 OFWBUS_PNP_INFO(compat_data);

Cache object: e3ff6fa64ec095a0b5c242bb4362b4d4


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.