linux/drivers/video/backlight/ktd253-backlight.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Backlight driver for the Kinetic KTD253
   4 * Based on code and know-how from the Samsung GT-S7710
   5 * Gareth Phillips <gareth.phillips@samsung.com>
   6 */
   7#include <linux/backlight.h>
   8#include <linux/delay.h>
   9#include <linux/err.h>
  10#include <linux/fb.h>
  11#include <linux/gpio/consumer.h>
  12#include <linux/init.h>
  13#include <linux/kernel.h>
  14#include <linux/limits.h>
  15#include <linux/module.h>
  16#include <linux/of.h>
  17#include <linux/platform_device.h>
  18#include <linux/property.h>
  19#include <linux/slab.h>
  20
  21/* Current ratio is n/32 from 1/32 to 32/32 */
  22#define KTD253_MIN_RATIO 1
  23#define KTD253_MAX_RATIO 32
  24#define KTD253_DEFAULT_RATIO 13
  25
  26#define KTD253_T_LOW_NS (200 + 10) /* Additional 10ns as safety factor */
  27#define KTD253_T_HIGH_NS (200 + 10) /* Additional 10ns as safety factor */
  28#define KTD253_T_OFF_CRIT_NS 100000 /* 100 us, now it doesn't look good */
  29#define KTD253_T_OFF_MS 3
  30
  31struct ktd253_backlight {
  32        struct device *dev;
  33        struct backlight_device *bl;
  34        struct gpio_desc *gpiod;
  35        u16 ratio;
  36};
  37
  38static void ktd253_backlight_set_max_ratio(struct ktd253_backlight *ktd253)
  39{
  40        gpiod_set_value_cansleep(ktd253->gpiod, 1);
  41        ndelay(KTD253_T_HIGH_NS);
  42        /* We always fall back to this when we power on */
  43}
  44
  45static int ktd253_backlight_stepdown(struct ktd253_backlight *ktd253)
  46{
  47        /*
  48         * These GPIO operations absolutely can NOT sleep so no _cansleep
  49         * suffixes, and no using GPIO expanders on slow buses for this!
  50         *
  51         * The maximum number of cycles of the loop is 32  so the time taken
  52         * should nominally be:
  53         * (T_LOW_NS + T_HIGH_NS + loop_time) * 32
  54         *
  55         * Architectures do not always support ndelay() and we will get a few us
  56         * instead. If we get to a critical time limit an interrupt has likely
  57         * occured in the low part of the loop and we need to restart from the
  58         * top so we have the backlight in a known state.
  59         */
  60        u64 ns;
  61
  62        ns = ktime_get_ns();
  63        gpiod_set_value(ktd253->gpiod, 0);
  64        ndelay(KTD253_T_LOW_NS);
  65        gpiod_set_value(ktd253->gpiod, 1);
  66        ns = ktime_get_ns() - ns;
  67        if (ns >= KTD253_T_OFF_CRIT_NS) {
  68                dev_err(ktd253->dev, "PCM on backlight took too long (%llu ns)\n", ns);
  69                return -EAGAIN;
  70        }
  71        ndelay(KTD253_T_HIGH_NS);
  72        return 0;
  73}
  74
  75static int ktd253_backlight_update_status(struct backlight_device *bl)
  76{
  77        struct ktd253_backlight *ktd253 = bl_get_data(bl);
  78        int brightness = backlight_get_brightness(bl);
  79        u16 target_ratio;
  80        u16 current_ratio = ktd253->ratio;
  81        int ret;
  82
  83        dev_dbg(ktd253->dev, "new brightness/ratio: %d/32\n", brightness);
  84
  85        target_ratio = brightness;
  86
  87        if (target_ratio == current_ratio)
  88                /* This is already right */
  89                return 0;
  90
  91        if (target_ratio == 0) {
  92                gpiod_set_value_cansleep(ktd253->gpiod, 0);
  93                /*
  94                 * We need to keep the GPIO low for at least this long
  95                 * to actually switch the KTD253 off.
  96                 */
  97                msleep(KTD253_T_OFF_MS);
  98                ktd253->ratio = 0;
  99                return 0;
 100        }
 101
 102        if (current_ratio == 0) {
 103                ktd253_backlight_set_max_ratio(ktd253);
 104                current_ratio = KTD253_MAX_RATIO;
 105        }
 106
 107        while (current_ratio != target_ratio) {
 108                /*
 109                 * These GPIO operations absolutely can NOT sleep so no
 110                 * _cansleep suffixes, and no using GPIO expanders on
 111                 * slow buses for this!
 112                 */
 113                ret = ktd253_backlight_stepdown(ktd253);
 114                if (ret == -EAGAIN) {
 115                        /*
 116                         * Something disturbed the backlight setting code when
 117                         * running so we need to bring the PWM back to a known
 118                         * state. This shouldn't happen too much.
 119                         */
 120                        gpiod_set_value_cansleep(ktd253->gpiod, 0);
 121                        msleep(KTD253_T_OFF_MS);
 122                        ktd253_backlight_set_max_ratio(ktd253);
 123                        current_ratio = KTD253_MAX_RATIO;
 124                } else if (current_ratio == KTD253_MIN_RATIO) {
 125                        /* After 1/32 we loop back to 32/32 */
 126                        current_ratio = KTD253_MAX_RATIO;
 127                } else {
 128                        current_ratio--;
 129                }
 130        }
 131        ktd253->ratio = current_ratio;
 132
 133        dev_dbg(ktd253->dev, "new ratio set to %d/32\n", target_ratio);
 134
 135        return 0;
 136}
 137
 138static const struct backlight_ops ktd253_backlight_ops = {
 139        .options        = BL_CORE_SUSPENDRESUME,
 140        .update_status  = ktd253_backlight_update_status,
 141};
 142
 143static int ktd253_backlight_probe(struct platform_device *pdev)
 144{
 145        struct device *dev = &pdev->dev;
 146        struct backlight_device *bl;
 147        struct ktd253_backlight *ktd253;
 148        u32 max_brightness;
 149        u32 brightness;
 150        int ret;
 151
 152        ktd253 = devm_kzalloc(dev, sizeof(*ktd253), GFP_KERNEL);
 153        if (!ktd253)
 154                return -ENOMEM;
 155        ktd253->dev = dev;
 156
 157        ret = device_property_read_u32(dev, "max-brightness", &max_brightness);
 158        if (ret)
 159                max_brightness = KTD253_MAX_RATIO;
 160        if (max_brightness > KTD253_MAX_RATIO) {
 161                /* Clamp brightness to hardware max */
 162                dev_err(dev, "illegal max brightness specified\n");
 163                max_brightness = KTD253_MAX_RATIO;
 164        }
 165
 166        ret = device_property_read_u32(dev, "default-brightness", &brightness);
 167        if (ret)
 168                brightness = KTD253_DEFAULT_RATIO;
 169        if (brightness > max_brightness) {
 170                /* Clamp default brightness to max brightness */
 171                dev_err(dev, "default brightness exceeds max brightness\n");
 172                brightness = max_brightness;
 173        }
 174
 175        ktd253->gpiod = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
 176        if (IS_ERR(ktd253->gpiod)) {
 177                ret = PTR_ERR(ktd253->gpiod);
 178                if (ret != -EPROBE_DEFER)
 179                        dev_err(dev, "gpio line missing or invalid.\n");
 180                return ret;
 181        }
 182        gpiod_set_consumer_name(ktd253->gpiod, dev_name(dev));
 183        /* Bring backlight to a known off state */
 184        msleep(KTD253_T_OFF_MS);
 185
 186        bl = devm_backlight_device_register(dev, dev_name(dev), dev, ktd253,
 187                                            &ktd253_backlight_ops, NULL);
 188        if (IS_ERR(bl)) {
 189                dev_err(dev, "failed to register backlight\n");
 190                return PTR_ERR(bl);
 191        }
 192        bl->props.max_brightness = max_brightness;
 193        /* When we just enable the GPIO line we set max brightness */
 194        if (brightness) {
 195                bl->props.brightness = brightness;
 196                bl->props.power = FB_BLANK_UNBLANK;
 197        } else {
 198                bl->props.brightness = 0;
 199                bl->props.power = FB_BLANK_POWERDOWN;
 200        }
 201
 202        ktd253->bl = bl;
 203        platform_set_drvdata(pdev, bl);
 204        backlight_update_status(bl);
 205
 206        return 0;
 207}
 208
 209static const struct of_device_id ktd253_backlight_of_match[] = {
 210        { .compatible = "kinetic,ktd253" },
 211        { .compatible = "kinetic,ktd259" },
 212        { /* sentinel */ }
 213};
 214MODULE_DEVICE_TABLE(of, ktd253_backlight_of_match);
 215
 216static struct platform_driver ktd253_backlight_driver = {
 217        .driver = {
 218                .name = "ktd253-backlight",
 219                .of_match_table = ktd253_backlight_of_match,
 220        },
 221        .probe          = ktd253_backlight_probe,
 222};
 223module_platform_driver(ktd253_backlight_driver);
 224
 225MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
 226MODULE_DESCRIPTION("Kinetic KTD253 Backlight Driver");
 227MODULE_LICENSE("GPL");
 228MODULE_ALIAS("platform:ktd253-backlight");
 229