Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

backlight: Add Kinetic KTD253 backlight driver

The Kinetic KTD253 backlight driver is controlled with a
single GPIO line, but still supports a range of brightness
settings by sending fast pulses on the line.

This is based off the source code release for the Samsung
GT-S7710 mobile phone.

Cc: Sam Ravnborg <sam@ravnborg.org>
Reviewed-by: Daniel Thompson <daniel.thompson@linaro.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>

authored by

Linus Walleij and committed by
Lee Jones
5317f37e 299beae5

+213
+6
MAINTAINERS
··· 9722 9722 F: drivers/auxdisplay/ks0108.c 9723 9723 F: include/linux/ks0108.h 9724 9724 9725 + KTD253 BACKLIGHT DRIVER 9726 + M: Linus Walleij <linus.walleij@linaro.org> 9727 + S: Maintained 9728 + F: Documentation/devicetree/bindings/leds/backlight/kinetic,ktd253.yaml 9729 + F: drivers/video/backlight/ktd253-backlight.c 9730 + 9725 9731 L3MDEV 9726 9732 M: David Ahern <dsahern@kernel.org> 9727 9733 L: netdev@vger.kernel.org
+8
drivers/video/backlight/Kconfig
··· 182 182 computers. Say yes if you have one of the h3100/h3600/h3700 183 183 machines. 184 184 185 + config BACKLIGHT_KTD253 186 + tristate "Backlight Driver for Kinetic KTD253" 187 + depends on GPIOLIB || COMPILE_TEST 188 + help 189 + Say y to enabled the backlight driver for the Kinetic KTD253 190 + which is a 1-wire GPIO-controlled backlight found in some mobile 191 + phones. 192 + 185 193 config BACKLIGHT_LM3533 186 194 tristate "Backlight Driver for LM3533" 187 195 depends on MFD_LM3533
+1
drivers/video/backlight/Makefile
··· 35 35 obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o 36 36 obj-$(CONFIG_BACKLIGHT_HP700) += jornada720_bl.o 37 37 obj-$(CONFIG_BACKLIGHT_IPAQ_MICRO) += ipaq_micro_bl.o 38 + obj-$(CONFIG_BACKLIGHT_KTD253) += ktd253-backlight.o 38 39 obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_bl.o 39 40 obj-$(CONFIG_BACKLIGHT_LM3630A) += lm3630a_bl.o 40 41 obj-$(CONFIG_BACKLIGHT_LM3639) += lm3639_bl.o
+198
drivers/video/backlight/ktd253-backlight.c
··· 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_MS 3 29 + 30 + struct ktd253_backlight { 31 + struct device *dev; 32 + struct backlight_device *bl; 33 + struct gpio_desc *gpiod; 34 + u16 ratio; 35 + }; 36 + 37 + static int ktd253_backlight_update_status(struct backlight_device *bl) 38 + { 39 + struct ktd253_backlight *ktd253 = bl_get_data(bl); 40 + int brightness = backlight_get_brightness(bl); 41 + u16 target_ratio; 42 + u16 current_ratio = ktd253->ratio; 43 + unsigned long flags; 44 + 45 + dev_dbg(ktd253->dev, "new brightness/ratio: %d/32\n", brightness); 46 + 47 + target_ratio = brightness; 48 + 49 + if (target_ratio == current_ratio) 50 + /* This is already right */ 51 + return 0; 52 + 53 + if (target_ratio == 0) { 54 + gpiod_set_value_cansleep(ktd253->gpiod, 0); 55 + /* 56 + * We need to keep the GPIO low for at least this long 57 + * to actually switch the KTD253 off. 58 + */ 59 + msleep(KTD253_T_OFF_MS); 60 + ktd253->ratio = 0; 61 + return 0; 62 + } 63 + 64 + if (current_ratio == 0) { 65 + gpiod_set_value_cansleep(ktd253->gpiod, 1); 66 + ndelay(KTD253_T_HIGH_NS); 67 + /* We always fall back to this when we power on */ 68 + current_ratio = KTD253_MAX_RATIO; 69 + } 70 + 71 + /* 72 + * WARNING: 73 + * The loop to set the correct current level is performed 74 + * with interrupts disabled as it is timing critical. 75 + * The maximum number of cycles of the loop is 32 76 + * so the time taken will be (T_LOW_NS + T_HIGH_NS + loop_time) * 32, 77 + */ 78 + local_irq_save(flags); 79 + while (current_ratio != target_ratio) { 80 + /* 81 + * These GPIO operations absolutely can NOT sleep so no 82 + * _cansleep suffixes, and no using GPIO expanders on 83 + * slow buses for this! 84 + */ 85 + gpiod_set_value(ktd253->gpiod, 0); 86 + ndelay(KTD253_T_LOW_NS); 87 + gpiod_set_value(ktd253->gpiod, 1); 88 + ndelay(KTD253_T_HIGH_NS); 89 + /* After 1/32 we loop back to 32/32 */ 90 + if (current_ratio == KTD253_MIN_RATIO) 91 + current_ratio = KTD253_MAX_RATIO; 92 + else 93 + current_ratio--; 94 + } 95 + local_irq_restore(flags); 96 + ktd253->ratio = current_ratio; 97 + 98 + dev_dbg(ktd253->dev, "new ratio set to %d/32\n", target_ratio); 99 + 100 + return 0; 101 + } 102 + 103 + static const struct backlight_ops ktd253_backlight_ops = { 104 + .options = BL_CORE_SUSPENDRESUME, 105 + .update_status = ktd253_backlight_update_status, 106 + }; 107 + 108 + static int ktd253_backlight_probe(struct platform_device *pdev) 109 + { 110 + struct device *dev = &pdev->dev; 111 + struct backlight_device *bl; 112 + struct ktd253_backlight *ktd253; 113 + u32 max_brightness; 114 + u32 brightness; 115 + int ret; 116 + 117 + ktd253 = devm_kzalloc(dev, sizeof(*ktd253), GFP_KERNEL); 118 + if (!ktd253) 119 + return -ENOMEM; 120 + ktd253->dev = dev; 121 + 122 + ret = device_property_read_u32(dev, "max-brightness", &max_brightness); 123 + if (ret) 124 + max_brightness = KTD253_MAX_RATIO; 125 + if (max_brightness > KTD253_MAX_RATIO) { 126 + /* Clamp brightness to hardware max */ 127 + dev_err(dev, "illegal max brightness specified\n"); 128 + max_brightness = KTD253_MAX_RATIO; 129 + } 130 + 131 + ret = device_property_read_u32(dev, "default-brightness", &brightness); 132 + if (ret) 133 + brightness = KTD253_DEFAULT_RATIO; 134 + if (brightness > max_brightness) { 135 + /* Clamp default brightness to max brightness */ 136 + dev_err(dev, "default brightness exceeds max brightness\n"); 137 + brightness = max_brightness; 138 + } 139 + 140 + if (brightness) 141 + /* This will be the default ratio when the KTD253 is enabled */ 142 + ktd253->ratio = KTD253_MAX_RATIO; 143 + else 144 + ktd253->ratio = 0; 145 + 146 + ktd253->gpiod = devm_gpiod_get(dev, "enable", 147 + brightness ? GPIOD_OUT_HIGH : 148 + GPIOD_OUT_LOW); 149 + if (IS_ERR(ktd253->gpiod)) { 150 + ret = PTR_ERR(ktd253->gpiod); 151 + if (ret != -EPROBE_DEFER) 152 + dev_err(dev, "gpio line missing or invalid.\n"); 153 + return ret; 154 + } 155 + gpiod_set_consumer_name(ktd253->gpiod, dev_name(dev)); 156 + 157 + bl = devm_backlight_device_register(dev, dev_name(dev), dev, ktd253, 158 + &ktd253_backlight_ops, NULL); 159 + if (IS_ERR(bl)) { 160 + dev_err(dev, "failed to register backlight\n"); 161 + return PTR_ERR(bl); 162 + } 163 + bl->props.max_brightness = max_brightness; 164 + /* When we just enable the GPIO line we set max brightness */ 165 + if (brightness) { 166 + bl->props.brightness = brightness; 167 + bl->props.power = FB_BLANK_UNBLANK; 168 + } else { 169 + bl->props.brightness = 0; 170 + bl->props.power = FB_BLANK_POWERDOWN; 171 + } 172 + 173 + ktd253->bl = bl; 174 + platform_set_drvdata(pdev, bl); 175 + backlight_update_status(bl); 176 + 177 + return 0; 178 + } 179 + 180 + static const struct of_device_id ktd253_backlight_of_match[] = { 181 + { .compatible = "kinetic,ktd253" }, 182 + { /* sentinel */ } 183 + }; 184 + MODULE_DEVICE_TABLE(of, ktd253_backlight_of_match); 185 + 186 + static struct platform_driver ktd253_backlight_driver = { 187 + .driver = { 188 + .name = "ktd253-backlight", 189 + .of_match_table = ktd253_backlight_of_match, 190 + }, 191 + .probe = ktd253_backlight_probe, 192 + }; 193 + module_platform_driver(ktd253_backlight_driver); 194 + 195 + MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); 196 + MODULE_DESCRIPTION("Kinetic KTD253 Backlight Driver"); 197 + MODULE_LICENSE("GPL"); 198 + MODULE_ALIAS("platform:ktd253-backlight");