blob: 81b3b910196bc7de814d0a49eddd585b9d9ea38f [file] [log] [blame]
Svyatoslav Ryhelb49f1cd2025-03-19 13:51:58 +02001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * TI LM3532 LED driver
4 *
5 * Copyright (c) 2019 Texas Instruments Incorporated
6 * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com>
7 */
8
9#define LOG_CATEGORY UCLASS_PANEL_BACKLIGHT
10
11#include <backlight.h>
12#include <dm.h>
13#include <dm/ofnode.h>
14#include <i2c.h>
15#include <log.h>
16#include <linux/delay.h>
17#include <linux/err.h>
18#include <asm/gpio.h>
19#include <power/regulator.h>
20
21#define LM3532_BL_MODE_MANUAL 0x00
22#define LM3532_BL_MODE_ALS 0x01
23
24#define LM3532_REG_OUTPUT_CFG 0x10
25#define LM3532_REG_STARTSHUT_RAMP 0x11
26#define LM3532_REG_RT_RAMP 0x12
27#define LM3532_REG_PWM_A_CFG 0x13
28#define LM3532_REG_PWM_B_CFG 0x14
29#define LM3532_REG_PWM_C_CFG 0x15
30#define LM3532_REG_ZONE_CFG_A 0x16
31#define LM3532_REG_CTRL_A_FS_CURR 0x17
32#define LM3532_REG_ZONE_CFG_B 0x18
33#define LM3532_REG_CTRL_B_FS_CURR 0x19
34#define LM3532_REG_ZONE_CFG_C 0x1a
35#define LM3532_REG_CTRL_C_FS_CURR 0x1b
36#define LM3532_REG_ENABLE 0x1d
37#define LM3532_ALS_CONFIG 0x23
38#define LM3532_REG_ZN_0_HI 0x60
39#define LM3532_REG_ZN_0_LO 0x61
40#define LM3532_REG_ZN_1_HI 0x62
41#define LM3532_REG_ZN_1_LO 0x63
42#define LM3532_REG_ZN_2_HI 0x64
43#define LM3532_REG_ZN_2_LO 0x65
44#define LM3532_REG_ZN_3_HI 0x66
45#define LM3532_REG_ZN_3_LO 0x67
46#define LM3532_REG_ZONE_TRGT_A 0x70
47#define LM3532_REG_ZONE_TRGT_B 0x75
48#define LM3532_REG_ZONE_TRGT_C 0x7a
49#define LM3532_REG_MAX 0x7e
50
51/* Control Enable */
52#define LM3532_CTRL_A_ENABLE BIT(0)
53#define LM3532_CTRL_B_ENABLE BIT(1)
54#define LM3532_CTRL_C_ENABLE BIT(2)
55
56/* PWM Zone Control */
57#define LM3532_PWM_ZONE_MASK 0x7c
58#define LM3532_PWM_ZONE_0_EN BIT(2)
59#define LM3532_PWM_ZONE_1_EN BIT(3)
60#define LM3532_PWM_ZONE_2_EN BIT(4)
61#define LM3532_PWM_ZONE_3_EN BIT(5)
62#define LM3532_PWM_ZONE_4_EN BIT(6)
63
64/* Brightness Configuration */
65#define LM3532_I2C_CTRL BIT(0)
66#define LM3532_ALS_CTRL 0
67#define LM3532_LINEAR_MAP BIT(1)
68#define LM3532_ZONE_MASK (BIT(2) | BIT(3) | BIT(4))
69#define LM3532_ZONE_0 0
70#define LM3532_ZONE_1 BIT(2)
71#define LM3532_ZONE_2 BIT(3)
72#define LM3532_ZONE_3 (BIT(2) | BIT(3))
73#define LM3532_ZONE_4 BIT(4)
74
75#define LM3532_ENABLE_ALS BIT(3)
76#define LM3532_ALS_SEL_SHIFT 6
77
78/* Zone Boundary Register */
79#define LM3532_ALS_WINDOW_mV 2000
80#define LM3532_ALS_ZB_MAX 4
81#define LM3532_ALS_OFFSET_mV 2
82
83#define LM3532_CONTROL_A 0
84#define LM3532_CONTROL_B 1
85#define LM3532_CONTROL_C 2
86#define LM3532_MAX_CONTROL_BANKS 3
87#define LM3532_MAX_LED_STRINGS 3
88
89#define LM3532_OUTPUT_CFG_MASK 0x3
90#define LM3532_BRT_VAL_ADJUST 8
91#define LM3532_RAMP_DOWN_SHIFT 3
92
93#define LM3532_NUM_RAMP_VALS 8
94#define LM3532_NUM_AVG_VALS 8
95#define LM3532_NUM_IMP_VALS 32
96
97#define LM3532_FS_CURR_MIN 5000
98#define LM3532_FS_CURR_MAX 29800
99#define LM3532_FS_CURR_STEP 800
100
101struct lm3532_bank_data {
102 int control_bank;
103 int mode;
104 int ctrl_brt_pointer;
105 int num_leds;
106 int full_scale_current;
107 u32 present:1;
108 u32 led_strings[LM3532_MAX_CONTROL_BANKS];
109};
110
111struct lm3532_backlight_priv {
112 struct gpio_desc enable_gpio;
113 struct udevice *regulator;
114
115 u32 runtime_ramp_up;
116 u32 runtime_ramp_down;
117
118 struct lm3532_bank_data bank[LM3532_MAX_CONTROL_BANKS];
119};
120
121/* This device does not like i2c md so use this instead */
122static void __maybe_unused dump_i2c(struct udevice *dev)
123{
124 int i, c;
125
126 for (i = 0; i < 0x10; i++) {
127 printf("00%02x: %02x", i * 0x10, dm_i2c_reg_read(dev, i * 0x10));
128 for (c = 1; c < 0xf; c++)
129 printf(" %02x", dm_i2c_reg_read(dev, i * 0x10 + c));
130 printf(" %02x\n", dm_i2c_reg_read(dev, i * 0x10 + 0xf));
131 }
132}
133
134static int lm3532_backlight_enable(struct udevice *dev)
135{
136 struct lm3532_backlight_priv *priv = dev_get_priv(dev);
137 int ret, i;
138
139 for (i = 0; i < LM3532_MAX_CONTROL_BANKS; i++) {
140 if (priv->bank[i].present) {
141 u32 ctrl_en_val = BIT(priv->bank[i].control_bank);
142
143 ret = dm_i2c_reg_clrset(dev, LM3532_REG_ENABLE,
144 ctrl_en_val, ctrl_en_val);
145 if (ret) {
146 log_debug("%s: failed to set ctrl: %d\n",
147 __func__, ret);
148 return ret;
149 }
150 }
151 }
152
153 regulator_set_enable_if_allowed(priv->regulator, 1);
154
155 return 0;
156}
157
158static int lm3532_backlight_set_brightness(struct udevice *dev, int percent)
159{
160 struct lm3532_backlight_priv *priv = dev_get_priv(dev);
161 struct lm3532_bank_data *bank;
162 int ret, i;
163
164 for (i = 0; i < LM3532_MAX_CONTROL_BANKS; i++) {
165 if (priv->bank[i].present) {
166 bank = &priv->bank[i];
167 u32 brightness_reg = LM3532_REG_ZONE_TRGT_A +
168 bank->control_bank * 5 +
169 (bank->ctrl_brt_pointer >> 2);
170
171 ret = dm_i2c_reg_write(dev, brightness_reg, percent);
172 if (ret) {
173 log_debug("%s: failed to set brightness: %d\n",
174 __func__, ret);
175 return ret;
176 }
177 }
178 }
179
180 return 0;
181}
182
183static const int ramp_table[LM3532_NUM_RAMP_VALS] = { 8, 1024, 2048, 4096, 8192,
184 16384, 32768, 65536 };
185static int lm3532_get_ramp_index(int ramp_time)
186{
187 int i;
188
189 if (ramp_time <= ramp_table[0])
190 return 0;
191
192 if (ramp_time > ramp_table[LM3532_NUM_RAMP_VALS - 1])
193 return LM3532_NUM_RAMP_VALS - 1;
194
195 for (i = 1; i < LM3532_NUM_RAMP_VALS; i++) {
196 if (ramp_time == ramp_table[i])
197 return i;
198
199 /* Find an approximate index by looking up the table */
200 if (ramp_time > ramp_table[i - 1] &&
201 ramp_time < ramp_table[i]) {
202 if (ramp_time - ramp_table[i - 1] < ramp_table[i] - ramp_time)
203 return i - 1;
204 else
205 return i;
206 }
207 }
208
209 return 0;
210}
211
212static int lm3532_backlight_of_to_plat(struct udevice *dev)
213{
214 struct lm3532_backlight_priv *priv = dev_get_priv(dev);
215 u32 ramp_time, reg;
216 ofnode child;
217 int ret;
218
219 ret = gpio_request_by_name(dev, "enable-gpios", 0,
220 &priv->enable_gpio, GPIOD_IS_OUT);
221 if (ret) {
222 log_debug("%s: could not decode enable-gpios (%d)\n", __func__, ret);
223 return ret;
224 }
225
226 ret = device_get_supply_regulator(dev, "vin-supply", &priv->regulator);
227 if (ret) {
228 log_debug("%s: vin regulator not defined: %d\n", __func__, ret);
229 if (ret != -ENOENT)
230 return log_ret(ret);
231 }
232
233 ramp_time = dev_read_u32_default(dev, "ramp-up-us", 0);
234 priv->runtime_ramp_up = lm3532_get_ramp_index(ramp_time);
235
236 ramp_time = dev_read_u32_default(dev, "ramp-down-us", 0);
237 priv->runtime_ramp_down = lm3532_get_ramp_index(ramp_time);
238
239 /* Backlight is one of children but has no dedicated driver */
240 ofnode_for_each_subnode(child, dev_ofnode(dev)) {
241 ret = ofnode_read_u32(child, "reg", &reg);
242 if (ret || reg > LM3532_CONTROL_C) {
243 log_debug("%s: control bank invalid %d\n", __func__, reg);
244 continue;
245 }
246
247 struct lm3532_bank_data *bank = &priv->bank[reg];
248
249 bank->control_bank = reg;
250 bank->present = 1;
251 bank->mode = ofnode_read_u32_default(child, "ti,led-mode",
252 LM3532_BL_MODE_MANUAL);
253 bank->mode = LM3532_BL_MODE_ALS ? LM3532_ALS_CTRL : LM3532_I2C_CTRL;
254
255 if (ofnode_read_bool(child, "ti,linear-mapping-mode"))
256 bank->mode |= LM3532_LINEAR_MAP;
257
258 bank->num_leds = ofnode_read_size(child, "led-sources");
259 bank->num_leds /= sizeof(u32);
260 if (bank->num_leds > LM3532_MAX_LED_STRINGS) {
261 log_debug("%s: too many LED string defined %d\n",
262 __func__, bank->num_leds);
263 continue;
264 }
265
266 ret = ofnode_read_u32_array(child, "led-sources",
267 bank->led_strings,
268 bank->num_leds);
269 if (ret) {
270 log_debug("%s: led-sources property missing %d\n",
271 __func__, ret);
272 continue;
273 }
274
275 ret = ofnode_read_u32(child, "led-max-microamp",
276 &bank->full_scale_current);
277 if (ret)
278 log_debug("%s: failed getting led-max-microamp %d\n",
279 __func__, ret);
280 else
281 bank->full_scale_current = min(bank->full_scale_current,
282 LM3532_FS_CURR_MAX);
283 }
284
285 return 0;
286}
287
288static int lm3532_backlight_init_registers(struct udevice *dev,
289 struct lm3532_bank_data *bank)
290{
291 struct lm3532_backlight_priv *priv = dev_get_priv(dev);
292 u32 brightness_config_val, runtime_ramp_val;
293 u32 output_cfg_val = 0, output_cfg_shift = 0, output_cfg_mask = 0;
294 int fs_current_reg, fs_current_val;
295 int ret, i;
296
297 if (!bank->present)
298 return 0;
299
300 u32 brightness_config_reg = LM3532_REG_ZONE_CFG_A + bank->control_bank * 2;
301 /*
302 * This could be hard coded to the default value but the control
303 * brightness register may have changed during boot.
304 */
305 ret = dm_i2c_reg_read(dev, brightness_config_reg);
306 if (ret < 0)
307 return ret;
308
309 bank->ctrl_brt_pointer = ret & ~LM3532_ZONE_MASK;
310 brightness_config_val = bank->ctrl_brt_pointer | bank->mode;
311
312 ret = dm_i2c_reg_write(dev, brightness_config_reg, brightness_config_val);
313 if (ret)
314 return ret;
315
316 if (bank->full_scale_current) {
317 fs_current_reg = LM3532_REG_CTRL_A_FS_CURR + bank->control_bank * 2;
318 fs_current_val = (bank->full_scale_current - LM3532_FS_CURR_MIN) /
319 LM3532_FS_CURR_STEP;
320
321 ret = dm_i2c_reg_write(dev, fs_current_reg, fs_current_val);
322 if (ret)
323 return ret;
324 }
325
326 for (i = 0; i < bank->num_leds; i++) {
327 output_cfg_shift = bank->led_strings[i] * 2;
328 output_cfg_val |= (bank->control_bank << output_cfg_shift);
329 output_cfg_mask |= LM3532_OUTPUT_CFG_MASK << output_cfg_shift;
330 }
331
332 ret = dm_i2c_reg_clrset(dev, LM3532_REG_OUTPUT_CFG, output_cfg_mask,
333 output_cfg_val);
334 if (ret)
335 return ret;
336
337 runtime_ramp_val = priv->runtime_ramp_up |
338 (priv->runtime_ramp_down << LM3532_RAMP_DOWN_SHIFT);
339
340 return dm_i2c_reg_write(dev, LM3532_REG_RT_RAMP, runtime_ramp_val);
341}
342
343static int lm3532_backlight_probe(struct udevice *dev)
344{
345 struct lm3532_backlight_priv *priv = dev_get_priv(dev);
346 int ret, i;
347
348 if (device_get_uclass_id(dev->parent) != UCLASS_I2C)
349 return -EPROTONOSUPPORT;
350
351 dm_gpio_set_value(&priv->enable_gpio, 1);
352
353 for (i = 0; i < LM3532_MAX_CONTROL_BANKS; i++) {
354 ret = lm3532_backlight_init_registers(dev, &priv->bank[i]);
355 if (ret)
356 return ret;
357 }
358
359 return 0;
360}
361
362static const struct backlight_ops lm3532_backlight_ops = {
363 .enable = lm3532_backlight_enable,
364 .set_brightness = lm3532_backlight_set_brightness,
365};
366
367static const struct udevice_id lm3532_backlight_ids[] = {
368 { .compatible = "ti,lm3532" },
369 { }
370};
371
372U_BOOT_DRIVER(lm3532_backlight) = {
373 .name = "lm3532_backlight",
374 .id = UCLASS_PANEL_BACKLIGHT,
375 .of_match = lm3532_backlight_ids,
376 .of_to_plat = lm3532_backlight_of_to_plat,
377 .probe = lm3532_backlight_probe,
378 .ops = &lm3532_backlight_ops,
379 .priv_auto = sizeof(struct lm3532_backlight_priv),
380};