]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
65fba592 SG |
2 | /* |
3 | * Copyright (c) 2016 Google, Inc | |
4 | * Written by Simon Glass <sjg@chromium.org> | |
65fba592 SG |
5 | */ |
6 | ||
a4f737a9 SG |
7 | #define LOG_CATEGORY UCLASS_PANEL_BACKLIGHT |
8 | ||
d678a59d | 9 | #include <common.h> |
65fba592 SG |
10 | #include <dm.h> |
11 | #include <backlight.h> | |
f7ae49fc | 12 | #include <log.h> |
336d4615 | 13 | #include <malloc.h> |
65fba592 SG |
14 | #include <pwm.h> |
15 | #include <asm/gpio.h> | |
c05ed00a | 16 | #include <linux/delay.h> |
8300cebc | 17 | #include <linux/math64.h> |
65fba592 SG |
18 | #include <power/regulator.h> |
19 | ||
a4f737a9 SG |
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) | |
f9b94055 | 37 | * @max_level: Maximum level of the backlight (full on) |
a4f737a9 SG |
38 | * @enabled: true if backlight is enabled |
39 | */ | |
65fba592 SG |
40 | struct pwm_backlight_priv { |
41 | struct udevice *reg; | |
42 | struct gpio_desc enable; | |
43 | struct udevice *pwm; | |
44 | uint channel; | |
45 | uint period_ns; | |
57e77754 SM |
46 | /* |
47 | * the polarity of one PWM | |
48 | * 0: normal polarity | |
49 | * 1: inverted polarity | |
50 | */ | |
51 | bool polarity; | |
a4f737a9 SG |
52 | u32 *levels; |
53 | int num_levels; | |
65fba592 | 54 | uint default_level; |
a4f737a9 | 55 | int cur_level; |
65fba592 SG |
56 | uint min_level; |
57 | uint max_level; | |
a4f737a9 | 58 | bool enabled; |
65fba592 SG |
59 | }; |
60 | ||
a4f737a9 | 61 | static int set_pwm(struct pwm_backlight_priv *priv) |
65fba592 | 62 | { |
8300cebc | 63 | u64 width; |
65fba592 SG |
64 | uint duty_cycle; |
65 | int ret; | |
66 | ||
fefa713b | 67 | if (priv->period_ns) { |
8300cebc AFG |
68 | width = priv->period_ns * (priv->cur_level - priv->min_level); |
69 | duty_cycle = div_u64(width, | |
70 | (priv->max_level - priv->min_level)); | |
fefa713b ANY |
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 | } | |
57e77754 SM |
79 | if (ret) |
80 | return log_ret(ret); | |
a4f737a9 | 81 | |
57e77754 | 82 | ret = pwm_set_invert(priv->pwm, priv->channel, priv->polarity); |
a2c4ef09 MD |
83 | if (ret == -ENOSYS && !priv->polarity) |
84 | ret = 0; | |
85 | ||
a4f737a9 SG |
86 | return log_ret(ret); |
87 | } | |
88 | ||
89 | static 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) { | |
caa4daa2 | 97 | __maybe_unused struct dm_regulator_uclass_plat |
a4f737a9 SG |
98 | *plat; |
99 | ||
caa4daa2 | 100 | plat = dev_get_uclass_plat(priv->reg); |
a4f737a9 SG |
101 | log_debug("Enable '%s', regulator '%s'/'%s'\n", |
102 | dev->name, priv->reg->name, plat->name); | |
251ff349 SR |
103 | ret = regulator_set_enable_if_allowed(priv->reg, true); |
104 | if (ret && ret != -ENOSYS) { | |
a4f737a9 | 105 | log_debug("Cannot enable regulator for PWM '%s'\n", |
4a978e86 | 106 | dev->name); |
a4f737a9 SG |
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 | ||
121 | static 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); | |
65fba592 | 130 | if (ret) |
a4f737a9 | 131 | return log_ret(ret); |
65fba592 SG |
132 | ret = pwm_set_enable(priv->pwm, priv->channel, true); |
133 | if (ret) | |
a4f737a9 SG |
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 | ||
143 | static 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); | |
251ff349 SR |
184 | ret = regulator_set_enable_if_allowed(priv->reg, false); |
185 | if (ret && ret != -ENOSYS) | |
186 | return log_ret(ret); | |
187 | ||
a4f737a9 SG |
188 | priv->enabled = false; |
189 | } | |
65fba592 SG |
190 | |
191 | return 0; | |
192 | } | |
193 | ||
d1998a9f | 194 | static int pwm_backlight_of_to_plat(struct udevice *dev) |
65fba592 SG |
195 | { |
196 | struct pwm_backlight_priv *priv = dev_get_priv(dev); | |
7cf208d7 | 197 | struct ofnode_phandle_args args; |
65fba592 SG |
198 | int index, ret, count, len; |
199 | const u32 *cell; | |
200 | ||
a4f737a9 | 201 | log_debug("start\n"); |
65fba592 SG |
202 | ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, |
203 | "power-supply", &priv->reg); | |
19f124d8 | 204 | if (ret) |
a4f737a9 | 205 | log_debug("Cannot get power supply: ret=%d\n", ret); |
65fba592 SG |
206 | ret = gpio_request_by_name(dev, "enable-gpios", 0, &priv->enable, |
207 | GPIOD_IS_OUT); | |
208 | if (ret) { | |
a4f737a9 | 209 | log_debug("Warning: cannot get enable GPIO: ret=%d\n", ret); |
65fba592 | 210 | if (ret != -ENOENT) |
a4f737a9 | 211 | return log_ret(ret); |
65fba592 | 212 | } |
7cf208d7 SG |
213 | ret = dev_read_phandle_with_args(dev, "pwms", "#pwm-cells", 0, 0, |
214 | &args); | |
65fba592 | 215 | if (ret) { |
a4f737a9 SG |
216 | log_debug("Cannot get PWM phandle: ret=%d\n", ret); |
217 | return log_ret(ret); | |
65fba592 SG |
218 | } |
219 | ||
7cf208d7 | 220 | ret = uclass_get_device_by_ofnode(UCLASS_PWM, args.node, &priv->pwm); |
65fba592 | 221 | if (ret) { |
a4f737a9 SG |
222 | log_debug("Cannot get PWM: ret=%d\n", ret); |
223 | return log_ret(ret); | |
65fba592 | 224 | } |
fefa713b | 225 | if (args.args_count < 1) |
a4f737a9 | 226 | return log_msg_ret("Not enough arguments to pwm\n", -EINVAL); |
65fba592 | 227 | priv->channel = args.args[0]; |
fefa713b ANY |
228 | if (args.args_count > 1) |
229 | priv->period_ns = args.args[1]; | |
57e77754 SM |
230 | if (args.args_count > 2) |
231 | priv->polarity = args.args[2]; | |
65fba592 | 232 | |
7cf208d7 SG |
233 | index = dev_read_u32_default(dev, "default-brightness-level", 255); |
234 | cell = dev_read_prop(dev, "brightness-levels", &len); | |
65fba592 SG |
235 | count = len / sizeof(u32); |
236 | if (cell && count > index) { | |
a4f737a9 SG |
237 | priv->levels = malloc(len); |
238 | if (!priv->levels) | |
239 | return log_ret(-ENOMEM); | |
92598bdb SG |
240 | ret = dev_read_u32_array(dev, "brightness-levels", priv->levels, |
241 | count); | |
242 | if (ret) | |
243 | return log_msg_ret("levels", ret); | |
a4f737a9 SG |
244 | priv->num_levels = count; |
245 | priv->default_level = priv->levels[index]; | |
246 | priv->max_level = priv->levels[count - 1]; | |
65fba592 SG |
247 | } else { |
248 | priv->default_level = index; | |
249 | priv->max_level = 255; | |
250 | } | |
a4f737a9 SG |
251 | priv->cur_level = priv->default_level; |
252 | log_debug("done\n"); | |
65fba592 SG |
253 | |
254 | ||
255 | return 0; | |
256 | } | |
257 | ||
258 | static int pwm_backlight_probe(struct udevice *dev) | |
259 | { | |
260 | return 0; | |
261 | } | |
262 | ||
263 | static const struct backlight_ops pwm_backlight_ops = { | |
a4f737a9 SG |
264 | .enable = pwm_backlight_enable, |
265 | .set_brightness = pwm_backlight_set_brightness, | |
65fba592 SG |
266 | }; |
267 | ||
268 | static const struct udevice_id pwm_backlight_ids[] = { | |
269 | { .compatible = "pwm-backlight" }, | |
270 | { } | |
271 | }; | |
272 | ||
273 | U_BOOT_DRIVER(pwm_backlight) = { | |
274 | .name = "pwm_backlight", | |
275 | .id = UCLASS_PANEL_BACKLIGHT, | |
276 | .of_match = pwm_backlight_ids, | |
277 | .ops = &pwm_backlight_ops, | |
d1998a9f | 278 | .of_to_plat = pwm_backlight_of_to_plat, |
65fba592 | 279 | .probe = pwm_backlight_probe, |
41575d8e | 280 | .priv_auto = sizeof(struct pwm_backlight_priv), |
65fba592 | 281 | }; |