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