1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* Copyright (c) 2015, Sony Mobile Communications, AB. |
3 | */ |
4 | |
5 | #include <linux/delay.h> |
6 | #include <linux/interrupt.h> |
7 | #include <linux/ktime.h> |
8 | #include <linux/kernel.h> |
9 | #include <linux/backlight.h> |
10 | #include <linux/module.h> |
11 | #include <linux/of.h> |
12 | #include <linux/of_address.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/regmap.h> |
15 | |
16 | /* From DT binding */ |
17 | #define WLED_MAX_STRINGS 4 |
18 | #define MOD_A 0 |
19 | #define MOD_B 1 |
20 | |
21 | #define WLED_DEFAULT_BRIGHTNESS 2048 |
22 | #define WLED_SOFT_START_DLY_US 10000 |
23 | #define WLED3_SINK_REG_BRIGHT_MAX 0xFFF |
24 | #define WLED5_SINK_REG_BRIGHT_MAX_12B 0xFFF |
25 | #define WLED5_SINK_REG_BRIGHT_MAX_15B 0x7FFF |
26 | |
27 | /* WLED3/WLED4 control registers */ |
28 | #define WLED3_CTRL_REG_FAULT_STATUS 0x08 |
29 | #define WLED3_CTRL_REG_ILIM_FAULT_BIT BIT(0) |
30 | #define WLED3_CTRL_REG_OVP_FAULT_BIT BIT(1) |
31 | #define WLED4_CTRL_REG_SC_FAULT_BIT BIT(2) |
32 | #define WLED5_CTRL_REG_OVP_PRE_ALARM_BIT BIT(4) |
33 | |
34 | #define WLED3_CTRL_REG_INT_RT_STS 0x10 |
35 | #define WLED3_CTRL_REG_OVP_FAULT_STATUS BIT(1) |
36 | |
37 | #define WLED3_CTRL_REG_MOD_EN 0x46 |
38 | #define WLED3_CTRL_REG_MOD_EN_MASK BIT(7) |
39 | #define WLED3_CTRL_REG_MOD_EN_SHIFT 7 |
40 | |
41 | #define WLED3_CTRL_REG_FEEDBACK_CONTROL 0x48 |
42 | |
43 | #define WLED3_CTRL_REG_FREQ 0x4c |
44 | #define WLED3_CTRL_REG_FREQ_MASK GENMASK(3, 0) |
45 | |
46 | #define WLED3_CTRL_REG_OVP 0x4d |
47 | #define WLED3_CTRL_REG_OVP_MASK GENMASK(1, 0) |
48 | #define WLED5_CTRL_REG_OVP_MASK GENMASK(3, 0) |
49 | |
50 | #define WLED3_CTRL_REG_ILIMIT 0x4e |
51 | #define WLED3_CTRL_REG_ILIMIT_MASK GENMASK(2, 0) |
52 | |
53 | /* WLED3/WLED4 sink registers */ |
54 | #define WLED3_SINK_REG_SYNC 0x47 |
55 | #define WLED3_SINK_REG_SYNC_CLEAR 0x00 |
56 | |
57 | #define WLED3_SINK_REG_CURR_SINK 0x4f |
58 | #define WLED3_SINK_REG_CURR_SINK_MASK GENMASK(7, 5) |
59 | #define WLED3_SINK_REG_CURR_SINK_SHFT 5 |
60 | |
61 | /* WLED3 specific per-'string' registers below */ |
62 | #define WLED3_SINK_REG_BRIGHT(n) (0x40 + n) |
63 | |
64 | #define WLED3_SINK_REG_STR_MOD_EN(n) (0x60 + (n * 0x10)) |
65 | #define WLED3_SINK_REG_STR_MOD_MASK BIT(7) |
66 | |
67 | #define WLED3_SINK_REG_STR_FULL_SCALE_CURR(n) (0x62 + (n * 0x10)) |
68 | #define WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK GENMASK(4, 0) |
69 | |
70 | #define WLED3_SINK_REG_STR_MOD_SRC(n) (0x63 + (n * 0x10)) |
71 | #define WLED3_SINK_REG_STR_MOD_SRC_MASK BIT(0) |
72 | #define WLED3_SINK_REG_STR_MOD_SRC_INT 0x00 |
73 | #define WLED3_SINK_REG_STR_MOD_SRC_EXT 0x01 |
74 | |
75 | #define WLED3_SINK_REG_STR_CABC(n) (0x66 + (n * 0x10)) |
76 | #define WLED3_SINK_REG_STR_CABC_MASK BIT(7) |
77 | |
78 | /* WLED4 specific control registers */ |
79 | #define WLED4_CTRL_REG_SHORT_PROTECT 0x5e |
80 | #define WLED4_CTRL_REG_SHORT_EN_MASK BIT(7) |
81 | |
82 | #define WLED4_CTRL_REG_SEC_ACCESS 0xd0 |
83 | #define WLED4_CTRL_REG_SEC_UNLOCK 0xa5 |
84 | |
85 | #define WLED4_CTRL_REG_TEST1 0xe2 |
86 | #define WLED4_CTRL_REG_TEST1_EXT_FET_DTEST2 0x09 |
87 | |
88 | /* WLED4 specific sink registers */ |
89 | #define WLED4_SINK_REG_CURR_SINK 0x46 |
90 | #define WLED4_SINK_REG_CURR_SINK_MASK GENMASK(7, 4) |
91 | #define WLED4_SINK_REG_CURR_SINK_SHFT 4 |
92 | |
93 | /* WLED4 specific per-'string' registers below */ |
94 | #define WLED4_SINK_REG_STR_MOD_EN(n) (0x50 + (n * 0x10)) |
95 | #define WLED4_SINK_REG_STR_MOD_MASK BIT(7) |
96 | |
97 | #define WLED4_SINK_REG_STR_FULL_SCALE_CURR(n) (0x52 + (n * 0x10)) |
98 | #define WLED4_SINK_REG_STR_FULL_SCALE_CURR_MASK GENMASK(3, 0) |
99 | |
100 | #define WLED4_SINK_REG_STR_MOD_SRC(n) (0x53 + (n * 0x10)) |
101 | #define WLED4_SINK_REG_STR_MOD_SRC_MASK BIT(0) |
102 | #define WLED4_SINK_REG_STR_MOD_SRC_INT 0x00 |
103 | #define WLED4_SINK_REG_STR_MOD_SRC_EXT 0x01 |
104 | |
105 | #define WLED4_SINK_REG_STR_CABC(n) (0x56 + (n * 0x10)) |
106 | #define WLED4_SINK_REG_STR_CABC_MASK BIT(7) |
107 | |
108 | #define WLED4_SINK_REG_BRIGHT(n) (0x57 + (n * 0x10)) |
109 | |
110 | /* WLED5 specific control registers */ |
111 | #define WLED5_CTRL_REG_OVP_INT_CTL 0x5f |
112 | #define WLED5_CTRL_REG_OVP_INT_TIMER_MASK GENMASK(2, 0) |
113 | |
114 | /* WLED5 specific sink registers */ |
115 | #define WLED5_SINK_REG_MOD_A_EN 0x50 |
116 | #define WLED5_SINK_REG_MOD_B_EN 0x60 |
117 | #define WLED5_SINK_REG_MOD_EN_MASK BIT(7) |
118 | |
119 | #define WLED5_SINK_REG_MOD_A_SRC_SEL 0x51 |
120 | #define WLED5_SINK_REG_MOD_B_SRC_SEL 0x61 |
121 | #define WLED5_SINK_REG_MOD_SRC_SEL_HIGH 0 |
122 | #define WLED5_SINK_REG_MOD_SRC_SEL_EXT 0x03 |
123 | #define WLED5_SINK_REG_MOD_SRC_SEL_MASK GENMASK(1, 0) |
124 | |
125 | #define WLED5_SINK_REG_MOD_A_BRIGHTNESS_WIDTH_SEL 0x52 |
126 | #define WLED5_SINK_REG_MOD_B_BRIGHTNESS_WIDTH_SEL 0x62 |
127 | #define WLED5_SINK_REG_BRIGHTNESS_WIDTH_12B 0 |
128 | #define WLED5_SINK_REG_BRIGHTNESS_WIDTH_15B 1 |
129 | |
130 | #define WLED5_SINK_REG_MOD_A_BRIGHTNESS_LSB 0x53 |
131 | #define WLED5_SINK_REG_MOD_A_BRIGHTNESS_MSB 0x54 |
132 | #define WLED5_SINK_REG_MOD_B_BRIGHTNESS_LSB 0x63 |
133 | #define WLED5_SINK_REG_MOD_B_BRIGHTNESS_MSB 0x64 |
134 | |
135 | #define WLED5_SINK_REG_MOD_SYNC_BIT 0x65 |
136 | #define WLED5_SINK_REG_SYNC_MOD_A_BIT BIT(0) |
137 | #define WLED5_SINK_REG_SYNC_MOD_B_BIT BIT(1) |
138 | #define WLED5_SINK_REG_SYNC_MASK GENMASK(1, 0) |
139 | |
140 | /* WLED5 specific per-'string' registers below */ |
141 | #define WLED5_SINK_REG_STR_FULL_SCALE_CURR(n) (0x72 + (n * 0x10)) |
142 | |
143 | #define WLED5_SINK_REG_STR_SRC_SEL(n) (0x73 + (n * 0x10)) |
144 | #define WLED5_SINK_REG_SRC_SEL_MOD_A 0 |
145 | #define WLED5_SINK_REG_SRC_SEL_MOD_B 1 |
146 | #define WLED5_SINK_REG_SRC_SEL_MASK GENMASK(1, 0) |
147 | |
148 | struct wled_var_cfg { |
149 | const u32 *values; |
150 | u32 (*fn)(u32); |
151 | int size; |
152 | }; |
153 | |
154 | struct wled_u32_opts { |
155 | const char *name; |
156 | u32 *val_ptr; |
157 | const struct wled_var_cfg *cfg; |
158 | }; |
159 | |
160 | struct wled_bool_opts { |
161 | const char *name; |
162 | bool *val_ptr; |
163 | }; |
164 | |
165 | struct wled_config { |
166 | u32 boost_i_limit; |
167 | u32 ovp; |
168 | u32 switch_freq; |
169 | u32 num_strings; |
170 | u32 string_i_limit; |
171 | u32 enabled_strings[WLED_MAX_STRINGS]; |
172 | u32 mod_sel; |
173 | u32 cabc_sel; |
174 | bool cs_out_en; |
175 | bool ext_gen; |
176 | bool cabc; |
177 | bool external_pfet; |
178 | bool auto_detection_enabled; |
179 | }; |
180 | |
181 | struct wled { |
182 | const char *name; |
183 | struct device *dev; |
184 | struct regmap *regmap; |
185 | struct mutex lock; /* Lock to avoid race from thread irq handler */ |
186 | ktime_t last_short_event; |
187 | ktime_t start_ovp_fault_time; |
188 | u16 ctrl_addr; |
189 | u16 sink_addr; |
190 | u16 max_string_count; |
191 | u16 auto_detection_ovp_count; |
192 | u32 brightness; |
193 | u32 max_brightness; |
194 | u32 short_count; |
195 | u32 auto_detect_count; |
196 | u32 version; |
197 | bool disabled_by_short; |
198 | bool has_short_detect; |
199 | bool cabc_disabled; |
200 | int short_irq; |
201 | int ovp_irq; |
202 | |
203 | struct wled_config cfg; |
204 | struct delayed_work ovp_work; |
205 | |
206 | /* Configures the brightness. Applicable for wled3, wled4 and wled5 */ |
207 | int (*wled_set_brightness)(struct wled *wled, u16 brightness); |
208 | |
209 | /* Configures the cabc register. Applicable for wled4 and wled5 */ |
210 | int (*wled_cabc_config)(struct wled *wled, bool enable); |
211 | |
212 | /* |
213 | * Toggles the sync bit for the brightness update to take place. |
214 | * Applicable for WLED3, WLED4 and WLED5. |
215 | */ |
216 | int (*wled_sync_toggle)(struct wled *wled); |
217 | |
218 | /* |
219 | * Time to wait before checking the OVP status after wled module enable. |
220 | * Applicable for WLED4 and WLED5. |
221 | */ |
222 | int (*wled_ovp_delay)(struct wled *wled); |
223 | |
224 | /* |
225 | * Determines if the auto string detection is required. |
226 | * Applicable for WLED4 and WLED5 |
227 | */ |
228 | bool (*wled_auto_detection_required)(struct wled *wled); |
229 | }; |
230 | |
231 | static int wled3_set_brightness(struct wled *wled, u16 brightness) |
232 | { |
233 | int rc, i; |
234 | __le16 v; |
235 | |
236 | v = cpu_to_le16(brightness & WLED3_SINK_REG_BRIGHT_MAX); |
237 | |
238 | for (i = 0; i < wled->cfg.num_strings; ++i) { |
239 | rc = regmap_bulk_write(map: wled->regmap, reg: wled->ctrl_addr + |
240 | WLED3_SINK_REG_BRIGHT(wled->cfg.enabled_strings[i]), |
241 | val: &v, val_count: sizeof(v)); |
242 | if (rc < 0) |
243 | return rc; |
244 | } |
245 | |
246 | return 0; |
247 | } |
248 | |
249 | static int wled4_set_brightness(struct wled *wled, u16 brightness) |
250 | { |
251 | int rc, i; |
252 | u16 low_limit = wled->max_brightness * 4 / 1000; |
253 | __le16 v; |
254 | |
255 | /* WLED4's lower limit of operation is 0.4% */ |
256 | if (brightness > 0 && brightness < low_limit) |
257 | brightness = low_limit; |
258 | |
259 | v = cpu_to_le16(brightness & WLED3_SINK_REG_BRIGHT_MAX); |
260 | |
261 | for (i = 0; i < wled->cfg.num_strings; ++i) { |
262 | rc = regmap_bulk_write(map: wled->regmap, reg: wled->sink_addr + |
263 | WLED4_SINK_REG_BRIGHT(wled->cfg.enabled_strings[i]), |
264 | val: &v, val_count: sizeof(v)); |
265 | if (rc < 0) |
266 | return rc; |
267 | } |
268 | |
269 | return 0; |
270 | } |
271 | |
272 | static int wled5_set_brightness(struct wled *wled, u16 brightness) |
273 | { |
274 | int rc, offset; |
275 | u16 low_limit = wled->max_brightness * 1 / 1000; |
276 | __le16 v; |
277 | |
278 | /* WLED5's lower limit is 0.1% */ |
279 | if (brightness < low_limit) |
280 | brightness = low_limit; |
281 | |
282 | v = cpu_to_le16(brightness & WLED5_SINK_REG_BRIGHT_MAX_15B); |
283 | |
284 | offset = (wled->cfg.mod_sel == MOD_A) ? |
285 | WLED5_SINK_REG_MOD_A_BRIGHTNESS_LSB : |
286 | WLED5_SINK_REG_MOD_B_BRIGHTNESS_LSB; |
287 | |
288 | rc = regmap_bulk_write(map: wled->regmap, reg: wled->sink_addr + offset, |
289 | val: &v, val_count: sizeof(v)); |
290 | return rc; |
291 | } |
292 | |
293 | static void wled_ovp_work(struct work_struct *work) |
294 | { |
295 | struct wled *wled = container_of(work, |
296 | struct wled, ovp_work.work); |
297 | enable_irq(irq: wled->ovp_irq); |
298 | } |
299 | |
300 | static int wled_module_enable(struct wled *wled, int val) |
301 | { |
302 | int rc; |
303 | |
304 | if (wled->disabled_by_short) |
305 | return -ENXIO; |
306 | |
307 | rc = regmap_update_bits(map: wled->regmap, reg: wled->ctrl_addr + |
308 | WLED3_CTRL_REG_MOD_EN, |
309 | WLED3_CTRL_REG_MOD_EN_MASK, |
310 | val: val << WLED3_CTRL_REG_MOD_EN_SHIFT); |
311 | if (rc < 0) |
312 | return rc; |
313 | |
314 | if (wled->ovp_irq > 0) { |
315 | if (val) { |
316 | /* |
317 | * The hardware generates a storm of spurious OVP |
318 | * interrupts during soft start operations. So defer |
319 | * enabling the IRQ for 10ms to ensure that the |
320 | * soft start is complete. |
321 | */ |
322 | schedule_delayed_work(dwork: &wled->ovp_work, HZ / 100); |
323 | } else { |
324 | if (!cancel_delayed_work_sync(dwork: &wled->ovp_work)) |
325 | disable_irq(irq: wled->ovp_irq); |
326 | } |
327 | } |
328 | |
329 | return 0; |
330 | } |
331 | |
332 | static int wled3_sync_toggle(struct wled *wled) |
333 | { |
334 | int rc; |
335 | unsigned int mask = GENMASK(wled->max_string_count - 1, 0); |
336 | |
337 | rc = regmap_update_bits(map: wled->regmap, |
338 | reg: wled->sink_addr + WLED3_SINK_REG_SYNC, |
339 | mask, WLED3_SINK_REG_SYNC_CLEAR); |
340 | if (rc < 0) |
341 | return rc; |
342 | |
343 | rc = regmap_update_bits(map: wled->regmap, |
344 | reg: wled->sink_addr + WLED3_SINK_REG_SYNC, |
345 | mask, val: mask); |
346 | |
347 | return rc; |
348 | } |
349 | |
350 | static int wled5_mod_sync_toggle(struct wled *wled) |
351 | { |
352 | int rc; |
353 | u8 val; |
354 | |
355 | rc = regmap_update_bits(map: wled->regmap, |
356 | reg: wled->sink_addr + WLED5_SINK_REG_MOD_SYNC_BIT, |
357 | WLED5_SINK_REG_SYNC_MASK, val: 0); |
358 | if (rc < 0) |
359 | return rc; |
360 | |
361 | val = (wled->cfg.mod_sel == MOD_A) ? WLED5_SINK_REG_SYNC_MOD_A_BIT : |
362 | WLED5_SINK_REG_SYNC_MOD_B_BIT; |
363 | return regmap_update_bits(map: wled->regmap, |
364 | reg: wled->sink_addr + WLED5_SINK_REG_MOD_SYNC_BIT, |
365 | WLED5_SINK_REG_SYNC_MASK, val); |
366 | } |
367 | |
368 | static int wled_ovp_fault_status(struct wled *wled, bool *fault_set) |
369 | { |
370 | int rc; |
371 | u32 int_rt_sts, fault_sts; |
372 | |
373 | *fault_set = false; |
374 | rc = regmap_read(map: wled->regmap, |
375 | reg: wled->ctrl_addr + WLED3_CTRL_REG_INT_RT_STS, |
376 | val: &int_rt_sts); |
377 | if (rc < 0) { |
378 | dev_err(wled->dev, "Failed to read INT_RT_STS rc=%d\n" , rc); |
379 | return rc; |
380 | } |
381 | |
382 | rc = regmap_read(map: wled->regmap, |
383 | reg: wled->ctrl_addr + WLED3_CTRL_REG_FAULT_STATUS, |
384 | val: &fault_sts); |
385 | if (rc < 0) { |
386 | dev_err(wled->dev, "Failed to read FAULT_STATUS rc=%d\n" , rc); |
387 | return rc; |
388 | } |
389 | |
390 | if (int_rt_sts & WLED3_CTRL_REG_OVP_FAULT_STATUS) |
391 | *fault_set = true; |
392 | |
393 | if (wled->version == 4 && (fault_sts & WLED3_CTRL_REG_OVP_FAULT_BIT)) |
394 | *fault_set = true; |
395 | |
396 | if (wled->version == 5 && (fault_sts & (WLED3_CTRL_REG_OVP_FAULT_BIT | |
397 | WLED5_CTRL_REG_OVP_PRE_ALARM_BIT))) |
398 | *fault_set = true; |
399 | |
400 | if (*fault_set) |
401 | dev_dbg(wled->dev, "WLED OVP fault detected, int_rt_sts=0x%x fault_sts=0x%x\n" , |
402 | int_rt_sts, fault_sts); |
403 | |
404 | return rc; |
405 | } |
406 | |
407 | static int wled4_ovp_delay(struct wled *wled) |
408 | { |
409 | return WLED_SOFT_START_DLY_US; |
410 | } |
411 | |
412 | static int wled5_ovp_delay(struct wled *wled) |
413 | { |
414 | int rc, delay_us; |
415 | u32 val; |
416 | u8 ovp_timer_ms[8] = {1, 2, 4, 8, 12, 16, 20, 24}; |
417 | |
418 | /* For WLED5, get the delay based on OVP timer */ |
419 | rc = regmap_read(map: wled->regmap, reg: wled->ctrl_addr + |
420 | WLED5_CTRL_REG_OVP_INT_CTL, val: &val); |
421 | if (rc < 0) |
422 | delay_us = |
423 | ovp_timer_ms[val & WLED5_CTRL_REG_OVP_INT_TIMER_MASK] * 1000; |
424 | else |
425 | delay_us = 2 * WLED_SOFT_START_DLY_US; |
426 | |
427 | dev_dbg(wled->dev, "delay_time_us: %d\n" , delay_us); |
428 | |
429 | return delay_us; |
430 | } |
431 | |
432 | static int wled_update_status(struct backlight_device *bl) |
433 | { |
434 | struct wled *wled = bl_get_data(bl_dev: bl); |
435 | u16 brightness = backlight_get_brightness(bd: bl); |
436 | int rc = 0; |
437 | |
438 | mutex_lock(&wled->lock); |
439 | if (brightness) { |
440 | rc = wled->wled_set_brightness(wled, brightness); |
441 | if (rc < 0) { |
442 | dev_err(wled->dev, "wled failed to set brightness rc:%d\n" , |
443 | rc); |
444 | goto unlock_mutex; |
445 | } |
446 | |
447 | if (wled->version < 5) { |
448 | rc = wled->wled_sync_toggle(wled); |
449 | if (rc < 0) { |
450 | dev_err(wled->dev, "wled sync failed rc:%d\n" , rc); |
451 | goto unlock_mutex; |
452 | } |
453 | } else { |
454 | /* |
455 | * For WLED5 toggling the MOD_SYNC_BIT updates the |
456 | * brightness |
457 | */ |
458 | rc = wled5_mod_sync_toggle(wled); |
459 | if (rc < 0) { |
460 | dev_err(wled->dev, "wled mod sync failed rc:%d\n" , |
461 | rc); |
462 | goto unlock_mutex; |
463 | } |
464 | } |
465 | } |
466 | |
467 | if (!!brightness != !!wled->brightness) { |
468 | rc = wled_module_enable(wled, val: !!brightness); |
469 | if (rc < 0) { |
470 | dev_err(wled->dev, "wled enable failed rc:%d\n" , rc); |
471 | goto unlock_mutex; |
472 | } |
473 | } |
474 | |
475 | wled->brightness = brightness; |
476 | |
477 | unlock_mutex: |
478 | mutex_unlock(lock: &wled->lock); |
479 | |
480 | return rc; |
481 | } |
482 | |
483 | static int wled4_cabc_config(struct wled *wled, bool enable) |
484 | { |
485 | int i, j, rc; |
486 | u8 val; |
487 | |
488 | for (i = 0; i < wled->cfg.num_strings; i++) { |
489 | j = wled->cfg.enabled_strings[i]; |
490 | |
491 | val = enable ? WLED4_SINK_REG_STR_CABC_MASK : 0; |
492 | rc = regmap_update_bits(map: wled->regmap, reg: wled->sink_addr + |
493 | WLED4_SINK_REG_STR_CABC(j), |
494 | WLED4_SINK_REG_STR_CABC_MASK, val); |
495 | if (rc < 0) |
496 | return rc; |
497 | } |
498 | |
499 | return 0; |
500 | } |
501 | |
502 | static int wled5_cabc_config(struct wled *wled, bool enable) |
503 | { |
504 | int rc, offset; |
505 | u8 reg; |
506 | |
507 | if (wled->cabc_disabled) |
508 | return 0; |
509 | |
510 | reg = enable ? wled->cfg.cabc_sel : 0; |
511 | offset = (wled->cfg.mod_sel == MOD_A) ? WLED5_SINK_REG_MOD_A_SRC_SEL : |
512 | WLED5_SINK_REG_MOD_B_SRC_SEL; |
513 | |
514 | rc = regmap_update_bits(map: wled->regmap, reg: wled->sink_addr + offset, |
515 | WLED5_SINK_REG_MOD_SRC_SEL_MASK, val: reg); |
516 | if (rc < 0) { |
517 | pr_err("Error in configuring CABC rc=%d\n" , rc); |
518 | return rc; |
519 | } |
520 | |
521 | if (!wled->cfg.cabc_sel) |
522 | wled->cabc_disabled = true; |
523 | |
524 | return 0; |
525 | } |
526 | |
527 | #define WLED_SHORT_DLY_MS 20 |
528 | #define WLED_SHORT_CNT_MAX 5 |
529 | #define WLED_SHORT_RESET_CNT_DLY_US USEC_PER_SEC |
530 | |
531 | static irqreturn_t wled_short_irq_handler(int irq, void *_wled) |
532 | { |
533 | struct wled *wled = _wled; |
534 | int rc; |
535 | s64 elapsed_time; |
536 | |
537 | wled->short_count++; |
538 | mutex_lock(&wled->lock); |
539 | rc = wled_module_enable(wled, val: false); |
540 | if (rc < 0) { |
541 | dev_err(wled->dev, "wled disable failed rc:%d\n" , rc); |
542 | goto unlock_mutex; |
543 | } |
544 | |
545 | elapsed_time = ktime_us_delta(later: ktime_get(), |
546 | earlier: wled->last_short_event); |
547 | if (elapsed_time > WLED_SHORT_RESET_CNT_DLY_US) |
548 | wled->short_count = 1; |
549 | |
550 | if (wled->short_count > WLED_SHORT_CNT_MAX) { |
551 | dev_err(wled->dev, "Short triggered %d times, disabling WLED forever!\n" , |
552 | wled->short_count); |
553 | wled->disabled_by_short = true; |
554 | goto unlock_mutex; |
555 | } |
556 | |
557 | wled->last_short_event = ktime_get(); |
558 | |
559 | msleep(WLED_SHORT_DLY_MS); |
560 | rc = wled_module_enable(wled, val: true); |
561 | if (rc < 0) |
562 | dev_err(wled->dev, "wled enable failed rc:%d\n" , rc); |
563 | |
564 | unlock_mutex: |
565 | mutex_unlock(lock: &wled->lock); |
566 | |
567 | return IRQ_HANDLED; |
568 | } |
569 | |
570 | #define AUTO_DETECT_BRIGHTNESS 200 |
571 | |
572 | static void wled_auto_string_detection(struct wled *wled) |
573 | { |
574 | int rc = 0, i, j, delay_time_us; |
575 | u32 sink_config = 0; |
576 | u8 sink_test = 0, sink_valid = 0, val; |
577 | bool fault_set; |
578 | |
579 | /* Read configured sink configuration */ |
580 | rc = regmap_read(map: wled->regmap, reg: wled->sink_addr + |
581 | WLED4_SINK_REG_CURR_SINK, val: &sink_config); |
582 | if (rc < 0) { |
583 | dev_err(wled->dev, "Failed to read SINK configuration rc=%d\n" , |
584 | rc); |
585 | goto failed_detect; |
586 | } |
587 | |
588 | /* Disable the module before starting detection */ |
589 | rc = regmap_update_bits(map: wled->regmap, |
590 | reg: wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN, |
591 | WLED3_CTRL_REG_MOD_EN_MASK, val: 0); |
592 | if (rc < 0) { |
593 | dev_err(wled->dev, "Failed to disable WLED module rc=%d\n" , rc); |
594 | goto failed_detect; |
595 | } |
596 | |
597 | /* Set low brightness across all sinks */ |
598 | rc = wled4_set_brightness(wled, AUTO_DETECT_BRIGHTNESS); |
599 | if (rc < 0) { |
600 | dev_err(wled->dev, "Failed to set brightness for auto detection rc=%d\n" , |
601 | rc); |
602 | goto failed_detect; |
603 | } |
604 | |
605 | if (wled->cfg.cabc) { |
606 | rc = wled->wled_cabc_config(wled, false); |
607 | if (rc < 0) |
608 | goto failed_detect; |
609 | } |
610 | |
611 | /* Disable all sinks */ |
612 | rc = regmap_write(map: wled->regmap, |
613 | reg: wled->sink_addr + WLED4_SINK_REG_CURR_SINK, val: 0); |
614 | if (rc < 0) { |
615 | dev_err(wled->dev, "Failed to disable all sinks rc=%d\n" , rc); |
616 | goto failed_detect; |
617 | } |
618 | |
619 | /* Iterate through the strings one by one */ |
620 | for (i = 0; i < wled->cfg.num_strings; i++) { |
621 | j = wled->cfg.enabled_strings[i]; |
622 | sink_test = BIT((WLED4_SINK_REG_CURR_SINK_SHFT + j)); |
623 | |
624 | /* Enable feedback control */ |
625 | rc = regmap_write(map: wled->regmap, reg: wled->ctrl_addr + |
626 | WLED3_CTRL_REG_FEEDBACK_CONTROL, val: j + 1); |
627 | if (rc < 0) { |
628 | dev_err(wled->dev, "Failed to enable feedback for SINK %d rc = %d\n" , |
629 | j + 1, rc); |
630 | goto failed_detect; |
631 | } |
632 | |
633 | /* Enable the sink */ |
634 | rc = regmap_write(map: wled->regmap, reg: wled->sink_addr + |
635 | WLED4_SINK_REG_CURR_SINK, val: sink_test); |
636 | if (rc < 0) { |
637 | dev_err(wled->dev, "Failed to configure SINK %d rc=%d\n" , |
638 | j + 1, rc); |
639 | goto failed_detect; |
640 | } |
641 | |
642 | /* Enable the module */ |
643 | rc = regmap_update_bits(map: wled->regmap, reg: wled->ctrl_addr + |
644 | WLED3_CTRL_REG_MOD_EN, |
645 | WLED3_CTRL_REG_MOD_EN_MASK, |
646 | WLED3_CTRL_REG_MOD_EN_MASK); |
647 | if (rc < 0) { |
648 | dev_err(wled->dev, "Failed to enable WLED module rc=%d\n" , |
649 | rc); |
650 | goto failed_detect; |
651 | } |
652 | |
653 | delay_time_us = wled->wled_ovp_delay(wled); |
654 | usleep_range(min: delay_time_us, max: delay_time_us + 1000); |
655 | |
656 | rc = wled_ovp_fault_status(wled, fault_set: &fault_set); |
657 | if (rc < 0) { |
658 | dev_err(wled->dev, "Error in getting OVP fault_sts, rc=%d\n" , |
659 | rc); |
660 | goto failed_detect; |
661 | } |
662 | |
663 | if (fault_set) |
664 | dev_dbg(wled->dev, "WLED OVP fault detected with SINK %d\n" , |
665 | j + 1); |
666 | else |
667 | sink_valid |= sink_test; |
668 | |
669 | /* Disable the module */ |
670 | rc = regmap_update_bits(map: wled->regmap, |
671 | reg: wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN, |
672 | WLED3_CTRL_REG_MOD_EN_MASK, val: 0); |
673 | if (rc < 0) { |
674 | dev_err(wled->dev, "Failed to disable WLED module rc=%d\n" , |
675 | rc); |
676 | goto failed_detect; |
677 | } |
678 | } |
679 | |
680 | if (!sink_valid) { |
681 | dev_err(wled->dev, "No valid WLED sinks found\n" ); |
682 | wled->disabled_by_short = true; |
683 | goto failed_detect; |
684 | } |
685 | |
686 | if (sink_valid != sink_config) { |
687 | dev_warn(wled->dev, "%x is not a valid sink configuration - using %x instead\n" , |
688 | sink_config, sink_valid); |
689 | sink_config = sink_valid; |
690 | } |
691 | |
692 | /* Write the new sink configuration */ |
693 | rc = regmap_write(map: wled->regmap, |
694 | reg: wled->sink_addr + WLED4_SINK_REG_CURR_SINK, |
695 | val: sink_config); |
696 | if (rc < 0) { |
697 | dev_err(wled->dev, "Failed to reconfigure the default sink rc=%d\n" , |
698 | rc); |
699 | goto failed_detect; |
700 | } |
701 | |
702 | /* Enable valid sinks */ |
703 | if (wled->version == 4) { |
704 | for (i = 0; i < wled->cfg.num_strings; i++) { |
705 | j = wled->cfg.enabled_strings[i]; |
706 | if (sink_config & |
707 | BIT(WLED4_SINK_REG_CURR_SINK_SHFT + j)) |
708 | val = WLED4_SINK_REG_STR_MOD_MASK; |
709 | else |
710 | /* Disable modulator_en for unused sink */ |
711 | val = 0; |
712 | |
713 | rc = regmap_write(map: wled->regmap, reg: wled->sink_addr + |
714 | WLED4_SINK_REG_STR_MOD_EN(j), val); |
715 | if (rc < 0) { |
716 | dev_err(wled->dev, "Failed to configure MODULATOR_EN rc=%d\n" , |
717 | rc); |
718 | goto failed_detect; |
719 | } |
720 | } |
721 | } |
722 | |
723 | /* Enable CABC */ |
724 | rc = wled->wled_cabc_config(wled, true); |
725 | if (rc < 0) |
726 | goto failed_detect; |
727 | |
728 | /* Restore the feedback setting */ |
729 | rc = regmap_write(map: wled->regmap, |
730 | reg: wled->ctrl_addr + WLED3_CTRL_REG_FEEDBACK_CONTROL, val: 0); |
731 | if (rc < 0) { |
732 | dev_err(wled->dev, "Failed to restore feedback setting rc=%d\n" , |
733 | rc); |
734 | goto failed_detect; |
735 | } |
736 | |
737 | /* Restore brightness */ |
738 | rc = wled4_set_brightness(wled, brightness: wled->brightness); |
739 | if (rc < 0) { |
740 | dev_err(wled->dev, "Failed to set brightness after auto detection rc=%d\n" , |
741 | rc); |
742 | goto failed_detect; |
743 | } |
744 | |
745 | rc = regmap_update_bits(map: wled->regmap, |
746 | reg: wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN, |
747 | WLED3_CTRL_REG_MOD_EN_MASK, |
748 | WLED3_CTRL_REG_MOD_EN_MASK); |
749 | if (rc < 0) { |
750 | dev_err(wled->dev, "Failed to enable WLED module rc=%d\n" , rc); |
751 | goto failed_detect; |
752 | } |
753 | |
754 | failed_detect: |
755 | return; |
756 | } |
757 | |
758 | #define WLED_AUTO_DETECT_OVP_COUNT 5 |
759 | #define WLED_AUTO_DETECT_CNT_DLY_US USEC_PER_SEC |
760 | |
761 | static bool wled4_auto_detection_required(struct wled *wled) |
762 | { |
763 | s64 elapsed_time_us; |
764 | |
765 | if (!wled->cfg.auto_detection_enabled) |
766 | return false; |
767 | |
768 | /* |
769 | * Check if the OVP fault was an occasional one |
770 | * or if it's firing continuously, the latter qualifies |
771 | * for an auto-detection check. |
772 | */ |
773 | if (!wled->auto_detection_ovp_count) { |
774 | wled->start_ovp_fault_time = ktime_get(); |
775 | wled->auto_detection_ovp_count++; |
776 | } else { |
777 | elapsed_time_us = ktime_us_delta(later: ktime_get(), |
778 | earlier: wled->start_ovp_fault_time); |
779 | if (elapsed_time_us > WLED_AUTO_DETECT_CNT_DLY_US) |
780 | wled->auto_detection_ovp_count = 0; |
781 | else |
782 | wled->auto_detection_ovp_count++; |
783 | |
784 | if (wled->auto_detection_ovp_count >= |
785 | WLED_AUTO_DETECT_OVP_COUNT) { |
786 | wled->auto_detection_ovp_count = 0; |
787 | return true; |
788 | } |
789 | } |
790 | |
791 | return false; |
792 | } |
793 | |
794 | static bool wled5_auto_detection_required(struct wled *wled) |
795 | { |
796 | if (!wled->cfg.auto_detection_enabled) |
797 | return false; |
798 | |
799 | /* |
800 | * Unlike WLED4, WLED5 has OVP fault density interrupt configuration |
801 | * i.e. to count the number of OVP alarms for a certain duration before |
802 | * triggering OVP fault interrupt. By default, number of OVP fault |
803 | * events counted before an interrupt is fired is 32 and the time |
804 | * interval is 12 ms. If we see one OVP fault interrupt, then that |
805 | * should qualify for a real OVP fault condition to run auto detection |
806 | * algorithm. |
807 | */ |
808 | return true; |
809 | } |
810 | |
811 | static int wled_auto_detection_at_init(struct wled *wled) |
812 | { |
813 | int rc; |
814 | bool fault_set; |
815 | |
816 | if (!wled->cfg.auto_detection_enabled) |
817 | return 0; |
818 | |
819 | rc = wled_ovp_fault_status(wled, fault_set: &fault_set); |
820 | if (rc < 0) { |
821 | dev_err(wled->dev, "Error in getting OVP fault_sts, rc=%d\n" , |
822 | rc); |
823 | return rc; |
824 | } |
825 | |
826 | if (fault_set) { |
827 | mutex_lock(&wled->lock); |
828 | wled_auto_string_detection(wled); |
829 | mutex_unlock(lock: &wled->lock); |
830 | } |
831 | |
832 | return rc; |
833 | } |
834 | |
835 | static irqreturn_t wled_ovp_irq_handler(int irq, void *_wled) |
836 | { |
837 | struct wled *wled = _wled; |
838 | int rc; |
839 | u32 int_sts, fault_sts; |
840 | |
841 | rc = regmap_read(map: wled->regmap, |
842 | reg: wled->ctrl_addr + WLED3_CTRL_REG_INT_RT_STS, val: &int_sts); |
843 | if (rc < 0) { |
844 | dev_err(wled->dev, "Error in reading WLED3_INT_RT_STS rc=%d\n" , |
845 | rc); |
846 | return IRQ_HANDLED; |
847 | } |
848 | |
849 | rc = regmap_read(map: wled->regmap, reg: wled->ctrl_addr + |
850 | WLED3_CTRL_REG_FAULT_STATUS, val: &fault_sts); |
851 | if (rc < 0) { |
852 | dev_err(wled->dev, "Error in reading WLED_FAULT_STATUS rc=%d\n" , |
853 | rc); |
854 | return IRQ_HANDLED; |
855 | } |
856 | |
857 | if (fault_sts & (WLED3_CTRL_REG_OVP_FAULT_BIT | |
858 | WLED3_CTRL_REG_ILIM_FAULT_BIT)) |
859 | dev_dbg(wled->dev, "WLED OVP fault detected, int_sts=%x fault_sts= %x\n" , |
860 | int_sts, fault_sts); |
861 | |
862 | if (fault_sts & WLED3_CTRL_REG_OVP_FAULT_BIT) { |
863 | if (wled->wled_auto_detection_required(wled)) { |
864 | mutex_lock(&wled->lock); |
865 | wled_auto_string_detection(wled); |
866 | mutex_unlock(lock: &wled->lock); |
867 | } |
868 | } |
869 | |
870 | return IRQ_HANDLED; |
871 | } |
872 | |
873 | static int wled3_setup(struct wled *wled) |
874 | { |
875 | u16 addr; |
876 | u8 sink_en = 0; |
877 | int rc, i, j; |
878 | |
879 | rc = regmap_update_bits(map: wled->regmap, |
880 | reg: wled->ctrl_addr + WLED3_CTRL_REG_OVP, |
881 | WLED3_CTRL_REG_OVP_MASK, val: wled->cfg.ovp); |
882 | if (rc) |
883 | return rc; |
884 | |
885 | rc = regmap_update_bits(map: wled->regmap, |
886 | reg: wled->ctrl_addr + WLED3_CTRL_REG_ILIMIT, |
887 | WLED3_CTRL_REG_ILIMIT_MASK, |
888 | val: wled->cfg.boost_i_limit); |
889 | if (rc) |
890 | return rc; |
891 | |
892 | rc = regmap_update_bits(map: wled->regmap, |
893 | reg: wled->ctrl_addr + WLED3_CTRL_REG_FREQ, |
894 | WLED3_CTRL_REG_FREQ_MASK, |
895 | val: wled->cfg.switch_freq); |
896 | if (rc) |
897 | return rc; |
898 | |
899 | for (i = 0; i < wled->cfg.num_strings; ++i) { |
900 | j = wled->cfg.enabled_strings[i]; |
901 | addr = wled->ctrl_addr + WLED3_SINK_REG_STR_MOD_EN(j); |
902 | rc = regmap_update_bits(map: wled->regmap, reg: addr, |
903 | WLED3_SINK_REG_STR_MOD_MASK, |
904 | WLED3_SINK_REG_STR_MOD_MASK); |
905 | if (rc) |
906 | return rc; |
907 | |
908 | if (wled->cfg.ext_gen) { |
909 | addr = wled->ctrl_addr + WLED3_SINK_REG_STR_MOD_SRC(j); |
910 | rc = regmap_update_bits(map: wled->regmap, reg: addr, |
911 | WLED3_SINK_REG_STR_MOD_SRC_MASK, |
912 | WLED3_SINK_REG_STR_MOD_SRC_EXT); |
913 | if (rc) |
914 | return rc; |
915 | } |
916 | |
917 | addr = wled->ctrl_addr + WLED3_SINK_REG_STR_FULL_SCALE_CURR(j); |
918 | rc = regmap_update_bits(map: wled->regmap, reg: addr, |
919 | WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK, |
920 | val: wled->cfg.string_i_limit); |
921 | if (rc) |
922 | return rc; |
923 | |
924 | addr = wled->ctrl_addr + WLED3_SINK_REG_STR_CABC(j); |
925 | rc = regmap_update_bits(map: wled->regmap, reg: addr, |
926 | WLED3_SINK_REG_STR_CABC_MASK, |
927 | val: wled->cfg.cabc ? |
928 | WLED3_SINK_REG_STR_CABC_MASK : 0); |
929 | if (rc) |
930 | return rc; |
931 | |
932 | sink_en |= BIT(j + WLED3_SINK_REG_CURR_SINK_SHFT); |
933 | } |
934 | |
935 | rc = regmap_update_bits(map: wled->regmap, |
936 | reg: wled->ctrl_addr + WLED3_SINK_REG_CURR_SINK, |
937 | WLED3_SINK_REG_CURR_SINK_MASK, val: sink_en); |
938 | if (rc) |
939 | return rc; |
940 | |
941 | return 0; |
942 | } |
943 | |
944 | static const struct wled_config wled3_config_defaults = { |
945 | .boost_i_limit = 3, |
946 | .string_i_limit = 20, |
947 | .ovp = 2, |
948 | .num_strings = 3, |
949 | .switch_freq = 5, |
950 | .cs_out_en = false, |
951 | .ext_gen = false, |
952 | .cabc = false, |
953 | .enabled_strings = {0, 1, 2}, |
954 | }; |
955 | |
956 | static int wled4_setup(struct wled *wled) |
957 | { |
958 | int rc, temp, i, j; |
959 | u16 addr; |
960 | u8 sink_en = 0; |
961 | u32 sink_cfg; |
962 | |
963 | rc = regmap_update_bits(map: wled->regmap, |
964 | reg: wled->ctrl_addr + WLED3_CTRL_REG_OVP, |
965 | WLED3_CTRL_REG_OVP_MASK, val: wled->cfg.ovp); |
966 | if (rc < 0) |
967 | return rc; |
968 | |
969 | rc = regmap_update_bits(map: wled->regmap, |
970 | reg: wled->ctrl_addr + WLED3_CTRL_REG_ILIMIT, |
971 | WLED3_CTRL_REG_ILIMIT_MASK, |
972 | val: wled->cfg.boost_i_limit); |
973 | if (rc < 0) |
974 | return rc; |
975 | |
976 | rc = regmap_update_bits(map: wled->regmap, |
977 | reg: wled->ctrl_addr + WLED3_CTRL_REG_FREQ, |
978 | WLED3_CTRL_REG_FREQ_MASK, |
979 | val: wled->cfg.switch_freq); |
980 | if (rc < 0) |
981 | return rc; |
982 | |
983 | if (wled->cfg.external_pfet) { |
984 | /* Unlock the secure register access */ |
985 | rc = regmap_write(map: wled->regmap, reg: wled->ctrl_addr + |
986 | WLED4_CTRL_REG_SEC_ACCESS, |
987 | WLED4_CTRL_REG_SEC_UNLOCK); |
988 | if (rc < 0) |
989 | return rc; |
990 | |
991 | rc = regmap_write(map: wled->regmap, |
992 | reg: wled->ctrl_addr + WLED4_CTRL_REG_TEST1, |
993 | WLED4_CTRL_REG_TEST1_EXT_FET_DTEST2); |
994 | if (rc < 0) |
995 | return rc; |
996 | } |
997 | |
998 | rc = regmap_read(map: wled->regmap, reg: wled->sink_addr + |
999 | WLED4_SINK_REG_CURR_SINK, val: &sink_cfg); |
1000 | if (rc < 0) |
1001 | return rc; |
1002 | |
1003 | for (i = 0; i < wled->cfg.num_strings; i++) { |
1004 | j = wled->cfg.enabled_strings[i]; |
1005 | temp = j + WLED4_SINK_REG_CURR_SINK_SHFT; |
1006 | sink_en |= 1 << temp; |
1007 | } |
1008 | |
1009 | if (sink_cfg == sink_en) { |
1010 | rc = wled_auto_detection_at_init(wled); |
1011 | return rc; |
1012 | } |
1013 | |
1014 | rc = regmap_update_bits(map: wled->regmap, |
1015 | reg: wled->sink_addr + WLED4_SINK_REG_CURR_SINK, |
1016 | WLED4_SINK_REG_CURR_SINK_MASK, val: 0); |
1017 | if (rc < 0) |
1018 | return rc; |
1019 | |
1020 | rc = regmap_update_bits(map: wled->regmap, reg: wled->ctrl_addr + |
1021 | WLED3_CTRL_REG_MOD_EN, |
1022 | WLED3_CTRL_REG_MOD_EN_MASK, val: 0); |
1023 | if (rc < 0) |
1024 | return rc; |
1025 | |
1026 | /* Per sink/string configuration */ |
1027 | for (i = 0; i < wled->cfg.num_strings; i++) { |
1028 | j = wled->cfg.enabled_strings[i]; |
1029 | |
1030 | addr = wled->sink_addr + |
1031 | WLED4_SINK_REG_STR_MOD_EN(j); |
1032 | rc = regmap_update_bits(map: wled->regmap, reg: addr, |
1033 | WLED4_SINK_REG_STR_MOD_MASK, |
1034 | WLED4_SINK_REG_STR_MOD_MASK); |
1035 | if (rc < 0) |
1036 | return rc; |
1037 | |
1038 | addr = wled->sink_addr + |
1039 | WLED4_SINK_REG_STR_FULL_SCALE_CURR(j); |
1040 | rc = regmap_update_bits(map: wled->regmap, reg: addr, |
1041 | WLED4_SINK_REG_STR_FULL_SCALE_CURR_MASK, |
1042 | val: wled->cfg.string_i_limit); |
1043 | if (rc < 0) |
1044 | return rc; |
1045 | } |
1046 | |
1047 | rc = wled4_cabc_config(wled, enable: wled->cfg.cabc); |
1048 | if (rc < 0) |
1049 | return rc; |
1050 | |
1051 | rc = regmap_update_bits(map: wled->regmap, reg: wled->ctrl_addr + |
1052 | WLED3_CTRL_REG_MOD_EN, |
1053 | WLED3_CTRL_REG_MOD_EN_MASK, |
1054 | WLED3_CTRL_REG_MOD_EN_MASK); |
1055 | if (rc < 0) |
1056 | return rc; |
1057 | |
1058 | rc = regmap_update_bits(map: wled->regmap, |
1059 | reg: wled->sink_addr + WLED4_SINK_REG_CURR_SINK, |
1060 | WLED4_SINK_REG_CURR_SINK_MASK, val: sink_en); |
1061 | if (rc < 0) |
1062 | return rc; |
1063 | |
1064 | rc = wled->wled_sync_toggle(wled); |
1065 | if (rc < 0) { |
1066 | dev_err(wled->dev, "Failed to toggle sync reg rc:%d\n" , rc); |
1067 | return rc; |
1068 | } |
1069 | |
1070 | rc = wled_auto_detection_at_init(wled); |
1071 | |
1072 | return rc; |
1073 | } |
1074 | |
1075 | static const struct wled_config wled4_config_defaults = { |
1076 | .boost_i_limit = 4, |
1077 | .string_i_limit = 10, |
1078 | .ovp = 1, |
1079 | .num_strings = 4, |
1080 | .switch_freq = 11, |
1081 | .cabc = false, |
1082 | .external_pfet = false, |
1083 | .auto_detection_enabled = false, |
1084 | .enabled_strings = {0, 1, 2, 3}, |
1085 | }; |
1086 | |
1087 | static int wled5_setup(struct wled *wled) |
1088 | { |
1089 | int rc, temp, i, j, offset; |
1090 | u8 sink_en = 0; |
1091 | u16 addr; |
1092 | u32 val; |
1093 | |
1094 | rc = regmap_update_bits(map: wled->regmap, |
1095 | reg: wled->ctrl_addr + WLED3_CTRL_REG_OVP, |
1096 | WLED5_CTRL_REG_OVP_MASK, val: wled->cfg.ovp); |
1097 | if (rc < 0) |
1098 | return rc; |
1099 | |
1100 | rc = regmap_update_bits(map: wled->regmap, |
1101 | reg: wled->ctrl_addr + WLED3_CTRL_REG_ILIMIT, |
1102 | WLED3_CTRL_REG_ILIMIT_MASK, |
1103 | val: wled->cfg.boost_i_limit); |
1104 | if (rc < 0) |
1105 | return rc; |
1106 | |
1107 | rc = regmap_update_bits(map: wled->regmap, |
1108 | reg: wled->ctrl_addr + WLED3_CTRL_REG_FREQ, |
1109 | WLED3_CTRL_REG_FREQ_MASK, |
1110 | val: wled->cfg.switch_freq); |
1111 | if (rc < 0) |
1112 | return rc; |
1113 | |
1114 | /* Per sink/string configuration */ |
1115 | for (i = 0; i < wled->cfg.num_strings; ++i) { |
1116 | j = wled->cfg.enabled_strings[i]; |
1117 | addr = wled->sink_addr + |
1118 | WLED4_SINK_REG_STR_FULL_SCALE_CURR(j); |
1119 | rc = regmap_update_bits(map: wled->regmap, reg: addr, |
1120 | WLED4_SINK_REG_STR_FULL_SCALE_CURR_MASK, |
1121 | val: wled->cfg.string_i_limit); |
1122 | if (rc < 0) |
1123 | return rc; |
1124 | |
1125 | addr = wled->sink_addr + WLED5_SINK_REG_STR_SRC_SEL(j); |
1126 | rc = regmap_update_bits(map: wled->regmap, reg: addr, |
1127 | WLED5_SINK_REG_SRC_SEL_MASK, |
1128 | val: wled->cfg.mod_sel == MOD_A ? |
1129 | WLED5_SINK_REG_SRC_SEL_MOD_A : |
1130 | WLED5_SINK_REG_SRC_SEL_MOD_B); |
1131 | |
1132 | temp = j + WLED4_SINK_REG_CURR_SINK_SHFT; |
1133 | sink_en |= 1 << temp; |
1134 | } |
1135 | |
1136 | rc = wled5_cabc_config(wled, enable: wled->cfg.cabc_sel ? true : false); |
1137 | if (rc < 0) |
1138 | return rc; |
1139 | |
1140 | /* Enable one of the modulators A or B based on mod_sel */ |
1141 | addr = wled->sink_addr + WLED5_SINK_REG_MOD_A_EN; |
1142 | val = (wled->cfg.mod_sel == MOD_A) ? WLED5_SINK_REG_MOD_EN_MASK : 0; |
1143 | rc = regmap_update_bits(map: wled->regmap, reg: addr, |
1144 | WLED5_SINK_REG_MOD_EN_MASK, val); |
1145 | if (rc < 0) |
1146 | return rc; |
1147 | |
1148 | addr = wled->sink_addr + WLED5_SINK_REG_MOD_B_EN; |
1149 | val = (wled->cfg.mod_sel == MOD_B) ? WLED5_SINK_REG_MOD_EN_MASK : 0; |
1150 | rc = regmap_update_bits(map: wled->regmap, reg: addr, |
1151 | WLED5_SINK_REG_MOD_EN_MASK, val); |
1152 | if (rc < 0) |
1153 | return rc; |
1154 | |
1155 | offset = (wled->cfg.mod_sel == MOD_A) ? |
1156 | WLED5_SINK_REG_MOD_A_BRIGHTNESS_WIDTH_SEL : |
1157 | WLED5_SINK_REG_MOD_B_BRIGHTNESS_WIDTH_SEL; |
1158 | |
1159 | addr = wled->sink_addr + offset; |
1160 | val = (wled->max_brightness == WLED5_SINK_REG_BRIGHT_MAX_15B) ? |
1161 | WLED5_SINK_REG_BRIGHTNESS_WIDTH_15B : |
1162 | WLED5_SINK_REG_BRIGHTNESS_WIDTH_12B; |
1163 | rc = regmap_write(map: wled->regmap, reg: addr, val); |
1164 | if (rc < 0) |
1165 | return rc; |
1166 | |
1167 | rc = regmap_update_bits(map: wled->regmap, |
1168 | reg: wled->sink_addr + WLED4_SINK_REG_CURR_SINK, |
1169 | WLED4_SINK_REG_CURR_SINK_MASK, val: sink_en); |
1170 | if (rc < 0) |
1171 | return rc; |
1172 | |
1173 | /* This updates only FSC configuration in WLED5 */ |
1174 | rc = wled->wled_sync_toggle(wled); |
1175 | if (rc < 0) { |
1176 | pr_err("Failed to toggle sync reg rc:%d\n" , rc); |
1177 | return rc; |
1178 | } |
1179 | |
1180 | rc = wled_auto_detection_at_init(wled); |
1181 | if (rc < 0) |
1182 | return rc; |
1183 | |
1184 | return 0; |
1185 | } |
1186 | |
1187 | static const struct wled_config wled5_config_defaults = { |
1188 | .boost_i_limit = 5, |
1189 | .string_i_limit = 10, |
1190 | .ovp = 4, |
1191 | .num_strings = 4, |
1192 | .switch_freq = 11, |
1193 | .mod_sel = 0, |
1194 | .cabc_sel = 0, |
1195 | .cabc = false, |
1196 | .external_pfet = false, |
1197 | .auto_detection_enabled = false, |
1198 | .enabled_strings = {0, 1, 2, 3}, |
1199 | }; |
1200 | |
1201 | static const u32 wled3_boost_i_limit_values[] = { |
1202 | 105, 385, 525, 805, 980, 1260, 1400, 1680, |
1203 | }; |
1204 | |
1205 | static const struct wled_var_cfg wled3_boost_i_limit_cfg = { |
1206 | .values = wled3_boost_i_limit_values, |
1207 | .size = ARRAY_SIZE(wled3_boost_i_limit_values), |
1208 | }; |
1209 | |
1210 | static const u32 wled4_boost_i_limit_values[] = { |
1211 | 105, 280, 450, 620, 970, 1150, 1300, 1500, |
1212 | }; |
1213 | |
1214 | static const struct wled_var_cfg wled4_boost_i_limit_cfg = { |
1215 | .values = wled4_boost_i_limit_values, |
1216 | .size = ARRAY_SIZE(wled4_boost_i_limit_values), |
1217 | }; |
1218 | |
1219 | static inline u32 wled5_boost_i_limit_values_fn(u32 idx) |
1220 | { |
1221 | return 525 + (idx * 175); |
1222 | } |
1223 | |
1224 | static const struct wled_var_cfg wled5_boost_i_limit_cfg = { |
1225 | .fn = wled5_boost_i_limit_values_fn, |
1226 | .size = 8, |
1227 | }; |
1228 | |
1229 | static const u32 wled3_ovp_values[] = { |
1230 | 35, 32, 29, 27, |
1231 | }; |
1232 | |
1233 | static const struct wled_var_cfg wled3_ovp_cfg = { |
1234 | .values = wled3_ovp_values, |
1235 | .size = ARRAY_SIZE(wled3_ovp_values), |
1236 | }; |
1237 | |
1238 | static const u32 wled4_ovp_values[] = { |
1239 | 31100, 29600, 19600, 18100, |
1240 | }; |
1241 | |
1242 | static const struct wled_var_cfg wled4_ovp_cfg = { |
1243 | .values = wled4_ovp_values, |
1244 | .size = ARRAY_SIZE(wled4_ovp_values), |
1245 | }; |
1246 | |
1247 | static inline u32 wled5_ovp_values_fn(u32 idx) |
1248 | { |
1249 | /* |
1250 | * 0000 - 38.5 V |
1251 | * 0001 - 37 V .. |
1252 | * 1111 - 16 V |
1253 | */ |
1254 | return 38500 - (idx * 1500); |
1255 | } |
1256 | |
1257 | static const struct wled_var_cfg wled5_ovp_cfg = { |
1258 | .fn = wled5_ovp_values_fn, |
1259 | .size = 16, |
1260 | }; |
1261 | |
1262 | static u32 wled3_switch_freq_values_fn(u32 idx) |
1263 | { |
1264 | return 19200 / (2 * (1 + idx)); |
1265 | } |
1266 | |
1267 | static const struct wled_var_cfg wled3_switch_freq_cfg = { |
1268 | .fn = wled3_switch_freq_values_fn, |
1269 | .size = 16, |
1270 | }; |
1271 | |
1272 | static const struct wled_var_cfg wled3_string_i_limit_cfg = { |
1273 | .size = 26, |
1274 | }; |
1275 | |
1276 | static const u32 wled4_string_i_limit_values[] = { |
1277 | 0, 2500, 5000, 7500, 10000, 12500, 15000, 17500, 20000, |
1278 | 22500, 25000, 27500, 30000, |
1279 | }; |
1280 | |
1281 | static const struct wled_var_cfg wled4_string_i_limit_cfg = { |
1282 | .values = wled4_string_i_limit_values, |
1283 | .size = ARRAY_SIZE(wled4_string_i_limit_values), |
1284 | }; |
1285 | |
1286 | static const struct wled_var_cfg wled5_mod_sel_cfg = { |
1287 | .size = 2, |
1288 | }; |
1289 | |
1290 | static const struct wled_var_cfg wled5_cabc_sel_cfg = { |
1291 | .size = 4, |
1292 | }; |
1293 | |
1294 | static u32 wled_values(const struct wled_var_cfg *cfg, u32 idx) |
1295 | { |
1296 | if (idx >= cfg->size) |
1297 | return UINT_MAX; |
1298 | if (cfg->fn) |
1299 | return cfg->fn(idx); |
1300 | if (cfg->values) |
1301 | return cfg->values[idx]; |
1302 | return idx; |
1303 | } |
1304 | |
1305 | static int wled_configure(struct wled *wled) |
1306 | { |
1307 | struct wled_config *cfg = &wled->cfg; |
1308 | struct device *dev = wled->dev; |
1309 | const __be32 *prop_addr; |
1310 | u32 size, val, c; |
1311 | int rc, i, j, string_len; |
1312 | |
1313 | const struct wled_u32_opts *u32_opts = NULL; |
1314 | const struct wled_u32_opts wled3_opts[] = { |
1315 | { |
1316 | .name = "qcom,current-boost-limit" , |
1317 | .val_ptr = &cfg->boost_i_limit, |
1318 | .cfg = &wled3_boost_i_limit_cfg, |
1319 | }, |
1320 | { |
1321 | .name = "qcom,current-limit" , |
1322 | .val_ptr = &cfg->string_i_limit, |
1323 | .cfg = &wled3_string_i_limit_cfg, |
1324 | }, |
1325 | { |
1326 | .name = "qcom,ovp" , |
1327 | .val_ptr = &cfg->ovp, |
1328 | .cfg = &wled3_ovp_cfg, |
1329 | }, |
1330 | { |
1331 | .name = "qcom,switching-freq" , |
1332 | .val_ptr = &cfg->switch_freq, |
1333 | .cfg = &wled3_switch_freq_cfg, |
1334 | }, |
1335 | }; |
1336 | |
1337 | const struct wled_u32_opts wled4_opts[] = { |
1338 | { |
1339 | .name = "qcom,current-boost-limit" , |
1340 | .val_ptr = &cfg->boost_i_limit, |
1341 | .cfg = &wled4_boost_i_limit_cfg, |
1342 | }, |
1343 | { |
1344 | .name = "qcom,current-limit-microamp" , |
1345 | .val_ptr = &cfg->string_i_limit, |
1346 | .cfg = &wled4_string_i_limit_cfg, |
1347 | }, |
1348 | { |
1349 | .name = "qcom,ovp-millivolt" , |
1350 | .val_ptr = &cfg->ovp, |
1351 | .cfg = &wled4_ovp_cfg, |
1352 | }, |
1353 | { |
1354 | .name = "qcom,switching-freq" , |
1355 | .val_ptr = &cfg->switch_freq, |
1356 | .cfg = &wled3_switch_freq_cfg, |
1357 | }, |
1358 | }; |
1359 | |
1360 | const struct wled_u32_opts wled5_opts[] = { |
1361 | { |
1362 | .name = "qcom,current-boost-limit" , |
1363 | .val_ptr = &cfg->boost_i_limit, |
1364 | .cfg = &wled5_boost_i_limit_cfg, |
1365 | }, |
1366 | { |
1367 | .name = "qcom,current-limit-microamp" , |
1368 | .val_ptr = &cfg->string_i_limit, |
1369 | .cfg = &wled4_string_i_limit_cfg, |
1370 | }, |
1371 | { |
1372 | .name = "qcom,ovp-millivolt" , |
1373 | .val_ptr = &cfg->ovp, |
1374 | .cfg = &wled5_ovp_cfg, |
1375 | }, |
1376 | { |
1377 | .name = "qcom,switching-freq" , |
1378 | .val_ptr = &cfg->switch_freq, |
1379 | .cfg = &wled3_switch_freq_cfg, |
1380 | }, |
1381 | { |
1382 | .name = "qcom,modulator-sel" , |
1383 | .val_ptr = &cfg->mod_sel, |
1384 | .cfg = &wled5_mod_sel_cfg, |
1385 | }, |
1386 | { |
1387 | .name = "qcom,cabc-sel" , |
1388 | .val_ptr = &cfg->cabc_sel, |
1389 | .cfg = &wled5_cabc_sel_cfg, |
1390 | }, |
1391 | }; |
1392 | |
1393 | const struct wled_bool_opts bool_opts[] = { |
1394 | { "qcom,cs-out" , &cfg->cs_out_en, }, |
1395 | { "qcom,ext-gen" , &cfg->ext_gen, }, |
1396 | { "qcom,cabc" , &cfg->cabc, }, |
1397 | { "qcom,external-pfet" , &cfg->external_pfet, }, |
1398 | { "qcom,auto-string-detection" , &cfg->auto_detection_enabled, }, |
1399 | }; |
1400 | |
1401 | prop_addr = of_get_address(dev: dev->of_node, index: 0, NULL, NULL); |
1402 | if (!prop_addr) { |
1403 | dev_err(wled->dev, "invalid IO resources\n" ); |
1404 | return -EINVAL; |
1405 | } |
1406 | wled->ctrl_addr = be32_to_cpu(*prop_addr); |
1407 | |
1408 | rc = of_property_read_string(np: dev->of_node, propname: "label" , out_string: &wled->name); |
1409 | if (rc) |
1410 | wled->name = devm_kasprintf(dev, GFP_KERNEL, fmt: "%pOFn" , dev->of_node); |
1411 | |
1412 | switch (wled->version) { |
1413 | case 3: |
1414 | u32_opts = wled3_opts; |
1415 | size = ARRAY_SIZE(wled3_opts); |
1416 | *cfg = wled3_config_defaults; |
1417 | wled->wled_set_brightness = wled3_set_brightness; |
1418 | wled->wled_sync_toggle = wled3_sync_toggle; |
1419 | wled->max_string_count = 3; |
1420 | wled->sink_addr = wled->ctrl_addr; |
1421 | break; |
1422 | |
1423 | case 4: |
1424 | u32_opts = wled4_opts; |
1425 | size = ARRAY_SIZE(wled4_opts); |
1426 | *cfg = wled4_config_defaults; |
1427 | wled->wled_set_brightness = wled4_set_brightness; |
1428 | wled->wled_sync_toggle = wled3_sync_toggle; |
1429 | wled->wled_cabc_config = wled4_cabc_config; |
1430 | wled->wled_ovp_delay = wled4_ovp_delay; |
1431 | wled->wled_auto_detection_required = |
1432 | wled4_auto_detection_required; |
1433 | wled->max_string_count = 4; |
1434 | |
1435 | prop_addr = of_get_address(dev: dev->of_node, index: 1, NULL, NULL); |
1436 | if (!prop_addr) { |
1437 | dev_err(wled->dev, "invalid IO resources\n" ); |
1438 | return -EINVAL; |
1439 | } |
1440 | wled->sink_addr = be32_to_cpu(*prop_addr); |
1441 | break; |
1442 | |
1443 | case 5: |
1444 | u32_opts = wled5_opts; |
1445 | size = ARRAY_SIZE(wled5_opts); |
1446 | *cfg = wled5_config_defaults; |
1447 | wled->wled_set_brightness = wled5_set_brightness; |
1448 | wled->wled_sync_toggle = wled3_sync_toggle; |
1449 | wled->wled_cabc_config = wled5_cabc_config; |
1450 | wled->wled_ovp_delay = wled5_ovp_delay; |
1451 | wled->wled_auto_detection_required = |
1452 | wled5_auto_detection_required; |
1453 | wled->max_string_count = 4; |
1454 | |
1455 | prop_addr = of_get_address(dev: dev->of_node, index: 1, NULL, NULL); |
1456 | if (!prop_addr) { |
1457 | dev_err(wled->dev, "invalid IO resources\n" ); |
1458 | return -EINVAL; |
1459 | } |
1460 | wled->sink_addr = be32_to_cpu(*prop_addr); |
1461 | break; |
1462 | |
1463 | default: |
1464 | dev_err(wled->dev, "Invalid WLED version\n" ); |
1465 | return -EINVAL; |
1466 | } |
1467 | |
1468 | for (i = 0; i < size; ++i) { |
1469 | rc = of_property_read_u32(np: dev->of_node, propname: u32_opts[i].name, out_value: &val); |
1470 | if (rc == -EINVAL) { |
1471 | continue; |
1472 | } else if (rc) { |
1473 | dev_err(dev, "error reading '%s'\n" , u32_opts[i].name); |
1474 | return rc; |
1475 | } |
1476 | |
1477 | c = UINT_MAX; |
1478 | for (j = 0; c != val; j++) { |
1479 | c = wled_values(cfg: u32_opts[i].cfg, idx: j); |
1480 | if (c == UINT_MAX) { |
1481 | dev_err(dev, "invalid value for '%s'\n" , |
1482 | u32_opts[i].name); |
1483 | return -EINVAL; |
1484 | } |
1485 | |
1486 | if (c == val) |
1487 | break; |
1488 | } |
1489 | |
1490 | dev_dbg(dev, "'%s' = %u\n" , u32_opts[i].name, c); |
1491 | *u32_opts[i].val_ptr = j; |
1492 | } |
1493 | |
1494 | for (i = 0; i < ARRAY_SIZE(bool_opts); ++i) { |
1495 | if (of_property_read_bool(np: dev->of_node, propname: bool_opts[i].name)) |
1496 | *bool_opts[i].val_ptr = true; |
1497 | } |
1498 | |
1499 | string_len = of_property_count_elems_of_size(np: dev->of_node, |
1500 | propname: "qcom,enabled-strings" , |
1501 | elem_size: sizeof(u32)); |
1502 | if (string_len > 0) { |
1503 | if (string_len > wled->max_string_count) { |
1504 | dev_err(dev, "Cannot have more than %d strings\n" , |
1505 | wled->max_string_count); |
1506 | return -EINVAL; |
1507 | } |
1508 | |
1509 | rc = of_property_read_u32_array(np: dev->of_node, |
1510 | propname: "qcom,enabled-strings" , |
1511 | out_values: wled->cfg.enabled_strings, |
1512 | sz: string_len); |
1513 | if (rc) { |
1514 | dev_err(dev, "Failed to read %d elements from qcom,enabled-strings: %d\n" , |
1515 | string_len, rc); |
1516 | return rc; |
1517 | } |
1518 | |
1519 | for (i = 0; i < string_len; ++i) { |
1520 | if (wled->cfg.enabled_strings[i] >= wled->max_string_count) { |
1521 | dev_err(dev, |
1522 | "qcom,enabled-strings index %d at %d is out of bounds\n" , |
1523 | wled->cfg.enabled_strings[i], i); |
1524 | return -EINVAL; |
1525 | } |
1526 | } |
1527 | |
1528 | cfg->num_strings = string_len; |
1529 | } |
1530 | |
1531 | rc = of_property_read_u32(np: dev->of_node, propname: "qcom,num-strings" , out_value: &val); |
1532 | if (!rc) { |
1533 | if (val < 1 || val > wled->max_string_count) { |
1534 | dev_err(dev, "qcom,num-strings must be between 1 and %d\n" , |
1535 | wled->max_string_count); |
1536 | return -EINVAL; |
1537 | } |
1538 | |
1539 | if (string_len > 0) { |
1540 | dev_warn(dev, "Only one of qcom,num-strings or qcom,enabled-strings" |
1541 | " should be set\n" ); |
1542 | if (val > string_len) { |
1543 | dev_err(dev, "qcom,num-strings exceeds qcom,enabled-strings\n" ); |
1544 | return -EINVAL; |
1545 | } |
1546 | } |
1547 | |
1548 | cfg->num_strings = val; |
1549 | } |
1550 | |
1551 | return 0; |
1552 | } |
1553 | |
1554 | static int wled_configure_short_irq(struct wled *wled, |
1555 | struct platform_device *pdev) |
1556 | { |
1557 | int rc; |
1558 | |
1559 | if (!wled->has_short_detect) |
1560 | return 0; |
1561 | |
1562 | rc = regmap_update_bits(map: wled->regmap, reg: wled->ctrl_addr + |
1563 | WLED4_CTRL_REG_SHORT_PROTECT, |
1564 | WLED4_CTRL_REG_SHORT_EN_MASK, |
1565 | WLED4_CTRL_REG_SHORT_EN_MASK); |
1566 | if (rc < 0) |
1567 | return rc; |
1568 | |
1569 | wled->short_irq = platform_get_irq_byname(pdev, "short" ); |
1570 | if (wled->short_irq < 0) { |
1571 | dev_dbg(&pdev->dev, "short irq is not used\n" ); |
1572 | return 0; |
1573 | } |
1574 | |
1575 | rc = devm_request_threaded_irq(dev: wled->dev, irq: wled->short_irq, |
1576 | NULL, thread_fn: wled_short_irq_handler, |
1577 | IRQF_ONESHOT, |
1578 | devname: "wled_short_irq" , dev_id: wled); |
1579 | if (rc < 0) |
1580 | dev_err(wled->dev, "Unable to request short_irq (err:%d)\n" , |
1581 | rc); |
1582 | |
1583 | return rc; |
1584 | } |
1585 | |
1586 | static int wled_configure_ovp_irq(struct wled *wled, |
1587 | struct platform_device *pdev) |
1588 | { |
1589 | int rc; |
1590 | u32 val; |
1591 | |
1592 | wled->ovp_irq = platform_get_irq_byname(pdev, "ovp" ); |
1593 | if (wled->ovp_irq < 0) { |
1594 | dev_dbg(&pdev->dev, "OVP IRQ not found - disabling automatic string detection\n" ); |
1595 | return 0; |
1596 | } |
1597 | |
1598 | rc = devm_request_threaded_irq(dev: wled->dev, irq: wled->ovp_irq, NULL, |
1599 | thread_fn: wled_ovp_irq_handler, IRQF_ONESHOT, |
1600 | devname: "wled_ovp_irq" , dev_id: wled); |
1601 | if (rc < 0) { |
1602 | dev_err(wled->dev, "Unable to request ovp_irq (err:%d)\n" , |
1603 | rc); |
1604 | wled->ovp_irq = 0; |
1605 | return 0; |
1606 | } |
1607 | |
1608 | rc = regmap_read(map: wled->regmap, reg: wled->ctrl_addr + |
1609 | WLED3_CTRL_REG_MOD_EN, val: &val); |
1610 | if (rc < 0) |
1611 | return rc; |
1612 | |
1613 | /* Keep OVP irq disabled until module is enabled */ |
1614 | if (!(val & WLED3_CTRL_REG_MOD_EN_MASK)) |
1615 | disable_irq(irq: wled->ovp_irq); |
1616 | |
1617 | return 0; |
1618 | } |
1619 | |
1620 | static const struct backlight_ops wled_ops = { |
1621 | .update_status = wled_update_status, |
1622 | }; |
1623 | |
1624 | static int wled_probe(struct platform_device *pdev) |
1625 | { |
1626 | struct backlight_properties props; |
1627 | struct backlight_device *bl; |
1628 | struct wled *wled; |
1629 | struct regmap *regmap; |
1630 | u32 val; |
1631 | int rc; |
1632 | |
1633 | regmap = dev_get_regmap(dev: pdev->dev.parent, NULL); |
1634 | if (!regmap) { |
1635 | dev_err(&pdev->dev, "Unable to get regmap\n" ); |
1636 | return -EINVAL; |
1637 | } |
1638 | |
1639 | wled = devm_kzalloc(dev: &pdev->dev, size: sizeof(*wled), GFP_KERNEL); |
1640 | if (!wled) |
1641 | return -ENOMEM; |
1642 | |
1643 | wled->regmap = regmap; |
1644 | wled->dev = &pdev->dev; |
1645 | |
1646 | wled->version = (uintptr_t)of_device_get_match_data(dev: &pdev->dev); |
1647 | if (!wled->version) { |
1648 | dev_err(&pdev->dev, "Unknown device version\n" ); |
1649 | return -ENODEV; |
1650 | } |
1651 | |
1652 | mutex_init(&wled->lock); |
1653 | rc = wled_configure(wled); |
1654 | if (rc) |
1655 | return rc; |
1656 | |
1657 | val = WLED3_SINK_REG_BRIGHT_MAX; |
1658 | of_property_read_u32(np: pdev->dev.of_node, propname: "max-brightness" , out_value: &val); |
1659 | wled->max_brightness = val; |
1660 | |
1661 | switch (wled->version) { |
1662 | case 3: |
1663 | wled->cfg.auto_detection_enabled = false; |
1664 | rc = wled3_setup(wled); |
1665 | if (rc) { |
1666 | dev_err(&pdev->dev, "wled3_setup failed\n" ); |
1667 | return rc; |
1668 | } |
1669 | break; |
1670 | |
1671 | case 4: |
1672 | wled->has_short_detect = true; |
1673 | rc = wled4_setup(wled); |
1674 | if (rc) { |
1675 | dev_err(&pdev->dev, "wled4_setup failed\n" ); |
1676 | return rc; |
1677 | } |
1678 | break; |
1679 | |
1680 | case 5: |
1681 | wled->has_short_detect = true; |
1682 | if (wled->cfg.cabc_sel) |
1683 | wled->max_brightness = WLED5_SINK_REG_BRIGHT_MAX_12B; |
1684 | |
1685 | rc = wled5_setup(wled); |
1686 | if (rc) { |
1687 | dev_err(&pdev->dev, "wled5_setup failed\n" ); |
1688 | return rc; |
1689 | } |
1690 | break; |
1691 | |
1692 | default: |
1693 | dev_err(wled->dev, "Invalid WLED version\n" ); |
1694 | break; |
1695 | } |
1696 | |
1697 | INIT_DELAYED_WORK(&wled->ovp_work, wled_ovp_work); |
1698 | |
1699 | rc = wled_configure_short_irq(wled, pdev); |
1700 | if (rc < 0) |
1701 | return rc; |
1702 | |
1703 | rc = wled_configure_ovp_irq(wled, pdev); |
1704 | if (rc < 0) |
1705 | return rc; |
1706 | |
1707 | val = WLED_DEFAULT_BRIGHTNESS; |
1708 | of_property_read_u32(np: pdev->dev.of_node, propname: "default-brightness" , out_value: &val); |
1709 | |
1710 | memset(&props, 0, sizeof(struct backlight_properties)); |
1711 | props.type = BACKLIGHT_RAW; |
1712 | props.brightness = val; |
1713 | props.max_brightness = wled->max_brightness; |
1714 | bl = devm_backlight_device_register(dev: &pdev->dev, name: wled->name, |
1715 | parent: &pdev->dev, devdata: wled, |
1716 | ops: &wled_ops, props: &props); |
1717 | return PTR_ERR_OR_ZERO(ptr: bl); |
1718 | }; |
1719 | |
1720 | static void wled_remove(struct platform_device *pdev) |
1721 | { |
1722 | struct wled *wled = platform_get_drvdata(pdev); |
1723 | |
1724 | mutex_destroy(lock: &wled->lock); |
1725 | cancel_delayed_work_sync(dwork: &wled->ovp_work); |
1726 | disable_irq(irq: wled->short_irq); |
1727 | disable_irq(irq: wled->ovp_irq); |
1728 | } |
1729 | |
1730 | static const struct of_device_id wled_match_table[] = { |
1731 | { .compatible = "qcom,pm8941-wled" , .data = (void *)3 }, |
1732 | { .compatible = "qcom,pmi8950-wled" , .data = (void *)4 }, |
1733 | { .compatible = "qcom,pmi8994-wled" , .data = (void *)4 }, |
1734 | { .compatible = "qcom,pmi8998-wled" , .data = (void *)4 }, |
1735 | { .compatible = "qcom,pm660l-wled" , .data = (void *)4 }, |
1736 | { .compatible = "qcom,pm6150l-wled" , .data = (void *)5 }, |
1737 | { .compatible = "qcom,pm8150l-wled" , .data = (void *)5 }, |
1738 | {} |
1739 | }; |
1740 | MODULE_DEVICE_TABLE(of, wled_match_table); |
1741 | |
1742 | static struct platform_driver wled_driver = { |
1743 | .probe = wled_probe, |
1744 | .remove_new = wled_remove, |
1745 | .driver = { |
1746 | .name = "qcom,wled" , |
1747 | .of_match_table = wled_match_table, |
1748 | }, |
1749 | }; |
1750 | |
1751 | module_platform_driver(wled_driver); |
1752 | |
1753 | MODULE_DESCRIPTION("Qualcomm WLED driver" ); |
1754 | MODULE_LICENSE("GPL v2" ); |
1755 | |