1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright (c) 2016 Google, Inc 4 * Written by Simon Glass <sjg@chromium.org> 5 */ 6 7#define LOG_CATEGORY UCLASS_PANEL_BACKLIGHT 8 9#include <common.h> 10#include <dm.h> 11#include <backlight.h> 12#include <log.h> 13#include <malloc.h> 14#include <pwm.h> 15#include <asm/gpio.h> 16#include <linux/delay.h> 17#include <linux/math64.h> 18#include <power/regulator.h> 19 20/** 21 * Private information for the PWM backlight 22 * 23 * If @num_levels is 0 then the levels are simple values with the backlight 24 * value going between the minimum (default 0) and the maximum (default 255). 25 * Otherwise the levels are an index into @levels (0..n-1). 26 * 27 * @reg: Regulator to enable to turn the backlight on (NULL if none) 28 * @enable, GPIO to set to enable the backlight (can be missing) 29 * @pwm: PWM to use to change the backlight brightness 30 * @channel: PWM channel to use 31 * @period_ns: Period of the backlight in nanoseconds 32 * @levels: Levels for the backlight, or NULL if not using indexed levels 33 * @num_levels: Number of levels 34 * @cur_level: Current level for the backlight (index or value) 35 * @default_level: Default level for the backlight (index or value) 36 * @min_level: Minimum level of the backlight (full off) 37 * @max_level: Maximum level of the backlight (full on) 38 * @enabled: true if backlight is enabled 39 */ 40struct pwm_backlight_priv { 41 struct udevice *reg; 42 struct gpio_desc enable; 43 struct udevice *pwm; 44 uint channel; 45 uint period_ns; 46 /* 47 * the polarity of one PWM 48 * 0: normal polarity 49 * 1: inverted polarity 50 */ 51 bool polarity; 52 u32 *levels; 53 int num_levels; 54 uint default_level; 55 int cur_level; 56 uint min_level; 57 uint max_level; 58 bool enabled; 59}; 60 61static int set_pwm(struct pwm_backlight_priv *priv) 62{ 63 u64 width; 64 uint duty_cycle; 65 int ret; 66 67 if (priv->period_ns) { 68 width = priv->period_ns * (priv->cur_level - priv->min_level); 69 duty_cycle = div_u64(width, 70 (priv->max_level - priv->min_level)); 71 ret = pwm_set_config(priv->pwm, priv->channel, priv->period_ns, 72 duty_cycle); 73 } else { 74 /* PWM driver will internally scale these like the above. */ 75 ret = pwm_set_config(priv->pwm, priv->channel, 76 priv->max_level - priv->min_level, 77 priv->cur_level - priv->min_level); 78 } 79 if (ret) 80 return log_ret(ret); 81 82 ret = pwm_set_invert(priv->pwm, priv->channel, priv->polarity); 83 if (ret == -ENOSYS && !priv->polarity) 84 ret = 0; 85 86 return log_ret(ret); 87} 88 89static int enable_sequence(struct udevice *dev, int seq) 90{ 91 struct pwm_backlight_priv *priv = dev_get_priv(dev); 92 int ret; 93 94 switch (seq) { 95 case 0: 96 if (priv->reg) { 97 __maybe_unused struct dm_regulator_uclass_plat 98 *plat; 99 100 plat = dev_get_uclass_plat(priv->reg); 101 log_debug("Enable '%s', regulator '%s'/'%s'\n", 102 dev->name, priv->reg->name, plat->name); 103 ret = regulator_set_enable_if_allowed(priv->reg, true); 104 if (ret && ret != -ENOSYS) { 105 log_debug("Cannot enable regulator for PWM '%s'\n", 106 dev->name); 107 return log_ret(ret); 108 } 109 mdelay(120); 110 } 111 break; 112 case 1: 113 mdelay(10); 114 dm_gpio_set_value(&priv->enable, 1); 115 break; 116 } 117 118 return 0; 119} 120 121static int pwm_backlight_enable(struct udevice *dev) 122{ 123 struct pwm_backlight_priv *priv = dev_get_priv(dev); 124 int ret; 125 126 ret = enable_sequence(dev, 0); 127 if (ret) 128 return log_ret(ret); 129 ret = set_pwm(priv); 130 if (ret) 131 return log_ret(ret); 132 ret = pwm_set_enable(priv->pwm, priv->channel, true); 133 if (ret) 134 return log_ret(ret); 135 ret = enable_sequence(dev, 1); 136 if (ret) 137 return log_ret(ret); 138 priv->enabled = true; 139 140 return 0; 141} 142 143static int pwm_backlight_set_brightness(struct udevice *dev, int percent) 144{ 145 struct pwm_backlight_priv *priv = dev_get_priv(dev); 146 bool disable = false; 147 int level; 148 int ret; 149 150 if (!priv->enabled) { 151 ret = enable_sequence(dev, 0); 152 if (ret) 153 return log_ret(ret); 154 } 155 if (percent == BACKLIGHT_OFF) { 156 disable = true; 157 percent = 0; 158 } 159 if (percent == BACKLIGHT_DEFAULT) { 160 level = priv->default_level; 161 } else { 162 if (priv->levels) { 163 level = priv->levels[percent * (priv->num_levels - 1) 164 / 100]; 165 } else { 166 level = priv->min_level + 167 (priv->max_level - priv->min_level) * 168 percent / 100; 169 } 170 } 171 priv->cur_level = level; 172 173 ret = set_pwm(priv); 174 if (ret) 175 return log_ret(ret); 176 if (!priv->enabled) { 177 ret = enable_sequence(dev, 1); 178 if (ret) 179 return log_ret(ret); 180 priv->enabled = true; 181 } 182 if (disable) { 183 dm_gpio_set_value(&priv->enable, 0); 184 ret = regulator_set_enable_if_allowed(priv->reg, false); 185 if (ret && ret != -ENOSYS) 186 return log_ret(ret); 187 188 priv->enabled = false; 189 } 190 191 return 0; 192} 193 194static int pwm_backlight_of_to_plat(struct udevice *dev) 195{ 196 struct pwm_backlight_priv *priv = dev_get_priv(dev); 197 struct ofnode_phandle_args args; 198 int index, ret, count, len; 199 const u32 *cell; 200 201 log_debug("start\n"); 202 ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, 203 "power-supply", &priv->reg); 204 if (ret) 205 log_debug("Cannot get power supply: ret=%d\n", ret); 206 ret = gpio_request_by_name(dev, "enable-gpios", 0, &priv->enable, 207 GPIOD_IS_OUT); 208 if (ret) { 209 log_debug("Warning: cannot get enable GPIO: ret=%d\n", ret); 210 if (ret != -ENOENT) 211 return log_ret(ret); 212 } 213 ret = dev_read_phandle_with_args(dev, "pwms", "#pwm-cells", 0, 0, 214 &args); 215 if (ret) { 216 log_debug("Cannot get PWM phandle: ret=%d\n", ret); 217 return log_ret(ret); 218 } 219 220 ret = uclass_get_device_by_ofnode(UCLASS_PWM, args.node, &priv->pwm); 221 if (ret) { 222 log_debug("Cannot get PWM: ret=%d\n", ret); 223 return log_ret(ret); 224 } 225 if (args.args_count < 1) 226 return log_msg_ret("Not enough arguments to pwm\n", -EINVAL); 227 priv->channel = args.args[0]; 228 if (args.args_count > 1) 229 priv->period_ns = args.args[1]; 230 if (args.args_count > 2) 231 priv->polarity = args.args[2]; 232 233 index = dev_read_u32_default(dev, "default-brightness-level", 255); 234 cell = dev_read_prop(dev, "brightness-levels", &len); 235 count = len / sizeof(u32); 236 if (cell && count > index) { 237 priv->levels = malloc(len); 238 if (!priv->levels) 239 return log_ret(-ENOMEM); 240 ret = dev_read_u32_array(dev, "brightness-levels", priv->levels, 241 count); 242 if (ret) 243 return log_msg_ret("levels", ret); 244 priv->num_levels = count; 245 priv->default_level = priv->levels[index]; 246 priv->max_level = priv->levels[count - 1]; 247 } else { 248 priv->default_level = index; 249 priv->max_level = 255; 250 } 251 priv->cur_level = priv->default_level; 252 log_debug("done\n"); 253 254 255 return 0; 256} 257 258static int pwm_backlight_probe(struct udevice *dev) 259{ 260 return 0; 261} 262 263static const struct backlight_ops pwm_backlight_ops = { 264 .enable = pwm_backlight_enable, 265 .set_brightness = pwm_backlight_set_brightness, 266}; 267 268static const struct udevice_id pwm_backlight_ids[] = { 269 { .compatible = "pwm-backlight" }, 270 { } 271}; 272 273U_BOOT_DRIVER(pwm_backlight) = { 274 .name = "pwm_backlight", 275 .id = UCLASS_PANEL_BACKLIGHT, 276 .of_match = pwm_backlight_ids, 277 .ops = &pwm_backlight_ops, 278 .of_to_plat = pwm_backlight_of_to_plat, 279 .probe = pwm_backlight_probe, 280 .priv_auto = sizeof(struct pwm_backlight_priv), 281}; 282