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