1 | // SPDX-License-Identifier: GPL-2.0-only |
---|---|
2 | /* |
3 | * Copyright (c) 2017-2022 Linaro Ltd |
4 | * Copyright (c) 2010-2012, The Linux Foundation. All rights reserved. |
5 | * Copyright (c) 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved. |
6 | */ |
7 | #include <linux/bits.h> |
8 | #include <linux/bitfield.h> |
9 | #include <linux/led-class-multicolor.h> |
10 | #include <linux/module.h> |
11 | #include <linux/nvmem-consumer.h> |
12 | #include <linux/of.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/pwm.h> |
15 | #include <linux/regmap.h> |
16 | #include <linux/slab.h> |
17 | #include <linux/soc/qcom/qcom-pbs.h> |
18 | |
19 | #define LPG_SUBTYPE_REG 0x05 |
20 | #define LPG_SUBTYPE_LPG 0x2 |
21 | #define LPG_SUBTYPE_PWM 0xb |
22 | #define LPG_SUBTYPE_HI_RES_PWM 0xc |
23 | #define LPG_SUBTYPE_LPG_LITE 0x11 |
24 | #define LPG_PATTERN_CONFIG_REG 0x40 |
25 | #define LPG_SIZE_CLK_REG 0x41 |
26 | #define PWM_CLK_SELECT_MASK GENMASK(1, 0) |
27 | #define PWM_SIZE_SELECT_MASK BIT(2) |
28 | #define PWM_CLK_SELECT_HI_RES_MASK GENMASK(2, 0) |
29 | #define PWM_SIZE_HI_RES_MASK GENMASK(6, 4) |
30 | #define LPG_PREDIV_CLK_REG 0x42 |
31 | #define PWM_FREQ_PRE_DIV_MASK GENMASK(6, 5) |
32 | #define PWM_FREQ_EXP_MASK GENMASK(2, 0) |
33 | #define PWM_TYPE_CONFIG_REG 0x43 |
34 | #define PWM_VALUE_REG 0x44 |
35 | #define PWM_ENABLE_CONTROL_REG 0x46 |
36 | #define PWM_SYNC_REG 0x47 |
37 | #define LPG_RAMP_DURATION_REG 0x50 |
38 | #define LPG_HI_PAUSE_REG 0x52 |
39 | #define LPG_LO_PAUSE_REG 0x54 |
40 | #define LPG_HI_IDX_REG 0x56 |
41 | #define LPG_LO_IDX_REG 0x57 |
42 | #define PWM_SEC_ACCESS_REG 0xd0 |
43 | #define PWM_DTEST_REG(x) (0xe2 + (x) - 1) |
44 | |
45 | #define SDAM_REG_PBS_SEQ_EN 0x42 |
46 | #define SDAM_PBS_TRIG_SET 0xe5 |
47 | #define SDAM_PBS_TRIG_CLR 0xe6 |
48 | |
49 | #define TRI_LED_SRC_SEL 0x45 |
50 | #define TRI_LED_EN_CTL 0x46 |
51 | #define TRI_LED_ATC_CTL 0x47 |
52 | |
53 | #define LPG_LUT_REG(x) (0x40 + (x) * 2) |
54 | #define RAMP_CONTROL_REG 0xc8 |
55 | |
56 | #define LPG_RESOLUTION_9BIT BIT(9) |
57 | #define LPG_RESOLUTION_15BIT BIT(15) |
58 | #define PPG_MAX_LED_BRIGHTNESS 255 |
59 | |
60 | #define LPG_MAX_M 7 |
61 | #define LPG_MAX_PREDIV 6 |
62 | |
63 | #define DEFAULT_TICK_DURATION_US 7800 |
64 | #define RAMP_STEP_DURATION(x) (((x) * 1000 / DEFAULT_TICK_DURATION_US) & 0xff) |
65 | |
66 | #define SDAM_MAX_DEVICES 2 |
67 | /* LPG common config settings for PPG */ |
68 | #define SDAM_START_BASE 0x40 |
69 | #define SDAM_REG_RAMP_STEP_DURATION 0x47 |
70 | |
71 | #define SDAM_LUT_SDAM_LUT_PATTERN_OFFSET 0x45 |
72 | #define SDAM_LPG_SDAM_LUT_PATTERN_OFFSET 0x80 |
73 | |
74 | /* LPG per channel config settings for PPG */ |
75 | #define SDAM_LUT_EN_OFFSET 0x0 |
76 | #define SDAM_PATTERN_CONFIG_OFFSET 0x1 |
77 | #define SDAM_END_INDEX_OFFSET 0x3 |
78 | #define SDAM_START_INDEX_OFFSET 0x4 |
79 | #define SDAM_PBS_SCRATCH_LUT_COUNTER_OFFSET 0x6 |
80 | #define SDAM_PAUSE_HI_MULTIPLIER_OFFSET 0x8 |
81 | #define SDAM_PAUSE_LO_MULTIPLIER_OFFSET 0x9 |
82 | |
83 | struct lpg_channel; |
84 | struct lpg_data; |
85 | |
86 | /** |
87 | * struct lpg - LPG device context |
88 | * @dev: pointer to LPG device |
89 | * @map: regmap for register access |
90 | * @lock: used to synchronize LED and pwm callback requests |
91 | * @pwm: PWM-chip object, if operating in PWM mode |
92 | * @data: reference to version specific data |
93 | * @lut_base: base address of the LUT block (optional) |
94 | * @lut_size: number of entries in the LUT block |
95 | * @lut_bitmap: allocation bitmap for LUT entries |
96 | * @pbs_dev: PBS device |
97 | * @lpg_chan_sdam: LPG SDAM peripheral device |
98 | * @lut_sdam: LUT SDAM peripheral device |
99 | * @pbs_en_bitmap: bitmap for tracking PBS triggers |
100 | * @triled_base: base address of the TRILED block (optional) |
101 | * @triled_src: power-source for the TRILED |
102 | * @triled_has_atc_ctl: true if there is TRI_LED_ATC_CTL register |
103 | * @triled_has_src_sel: true if there is TRI_LED_SRC_SEL register |
104 | * @channels: list of PWM channels |
105 | * @num_channels: number of @channels |
106 | */ |
107 | struct lpg { |
108 | struct device *dev; |
109 | struct regmap *map; |
110 | |
111 | struct mutex lock; |
112 | |
113 | struct pwm_chip *pwm; |
114 | |
115 | const struct lpg_data *data; |
116 | |
117 | u32 lut_base; |
118 | u32 lut_size; |
119 | unsigned long *lut_bitmap; |
120 | |
121 | struct pbs_dev *pbs_dev; |
122 | struct nvmem_device *lpg_chan_sdam; |
123 | struct nvmem_device *lut_sdam; |
124 | unsigned long pbs_en_bitmap; |
125 | |
126 | u32 triled_base; |
127 | u32 triled_src; |
128 | bool triled_has_atc_ctl; |
129 | bool triled_has_src_sel; |
130 | |
131 | struct lpg_channel *channels; |
132 | unsigned int num_channels; |
133 | }; |
134 | |
135 | /** |
136 | * struct lpg_channel - per channel data |
137 | * @lpg: reference to parent lpg |
138 | * @base: base address of the PWM channel |
139 | * @triled_mask: mask in TRILED to enable this channel |
140 | * @lut_mask: mask in LUT to start pattern generator for this channel |
141 | * @subtype: PMIC hardware block subtype |
142 | * @sdam_offset: channel offset in LPG SDAM |
143 | * @in_use: channel is exposed to LED framework |
144 | * @color: color of the LED attached to this channel |
145 | * @dtest_line: DTEST line for output, or 0 if disabled |
146 | * @dtest_value: DTEST line configuration |
147 | * @pwm_value: duty (in microseconds) of the generated pulses, overridden by LUT |
148 | * @enabled: output enabled? |
149 | * @period: period (in nanoseconds) of the generated pulses |
150 | * @clk_sel: reference clock frequency selector |
151 | * @pre_div_sel: divider selector of the reference clock |
152 | * @pre_div_exp: exponential divider of the reference clock |
153 | * @pwm_resolution_sel: pwm resolution selector |
154 | * @ramp_enabled: duty cycle is driven by iterating over lookup table |
155 | * @ramp_ping_pong: reverse through pattern, rather than wrapping to start |
156 | * @ramp_oneshot: perform only a single pass over the pattern |
157 | * @ramp_reverse: iterate over pattern backwards |
158 | * @ramp_tick_ms: length (in milliseconds) of one step in the pattern |
159 | * @ramp_lo_pause_ms: pause (in milliseconds) before iterating over pattern |
160 | * @ramp_hi_pause_ms: pause (in milliseconds) after iterating over pattern |
161 | * @pattern_lo_idx: start index of associated pattern |
162 | * @pattern_hi_idx: last index of associated pattern |
163 | */ |
164 | struct lpg_channel { |
165 | struct lpg *lpg; |
166 | |
167 | u32 base; |
168 | unsigned int triled_mask; |
169 | unsigned int lut_mask; |
170 | unsigned int subtype; |
171 | u32 sdam_offset; |
172 | |
173 | bool in_use; |
174 | |
175 | int color; |
176 | |
177 | u32 dtest_line; |
178 | u32 dtest_value; |
179 | |
180 | u16 pwm_value; |
181 | bool enabled; |
182 | |
183 | u64 period; |
184 | unsigned int clk_sel; |
185 | unsigned int pre_div_sel; |
186 | unsigned int pre_div_exp; |
187 | unsigned int pwm_resolution_sel; |
188 | |
189 | bool ramp_enabled; |
190 | bool ramp_ping_pong; |
191 | bool ramp_oneshot; |
192 | bool ramp_reverse; |
193 | unsigned short ramp_tick_ms; |
194 | unsigned long ramp_lo_pause_ms; |
195 | unsigned long ramp_hi_pause_ms; |
196 | |
197 | unsigned int pattern_lo_idx; |
198 | unsigned int pattern_hi_idx; |
199 | }; |
200 | |
201 | /** |
202 | * struct lpg_led - logical LED object |
203 | * @lpg: lpg context reference |
204 | * @cdev: LED class device |
205 | * @mcdev: Multicolor LED class device |
206 | * @num_channels: number of @channels |
207 | * @channels: list of channels associated with the LED |
208 | */ |
209 | struct lpg_led { |
210 | struct lpg *lpg; |
211 | |
212 | struct led_classdev cdev; |
213 | struct led_classdev_mc mcdev; |
214 | |
215 | unsigned int num_channels; |
216 | struct lpg_channel *channels[] __counted_by(num_channels); |
217 | }; |
218 | |
219 | /** |
220 | * struct lpg_channel_data - per channel initialization data |
221 | * @sdam_offset: Channel offset in LPG SDAM |
222 | * @base: base address for PWM channel registers |
223 | * @triled_mask: bitmask for controlling this channel in TRILED |
224 | */ |
225 | struct lpg_channel_data { |
226 | unsigned int sdam_offset; |
227 | unsigned int base; |
228 | u8 triled_mask; |
229 | }; |
230 | |
231 | /** |
232 | * struct lpg_data - initialization data |
233 | * @lut_base: base address of LUT block |
234 | * @lut_size: number of entries in LUT |
235 | * @triled_base: base address of TRILED |
236 | * @triled_has_atc_ctl: true if there is TRI_LED_ATC_CTL register |
237 | * @triled_has_src_sel: true if there is TRI_LED_SRC_SEL register |
238 | * @num_channels: number of channels in LPG |
239 | * @channels: list of channel initialization data |
240 | */ |
241 | struct lpg_data { |
242 | unsigned int lut_base; |
243 | unsigned int lut_size; |
244 | unsigned int triled_base; |
245 | bool triled_has_atc_ctl; |
246 | bool triled_has_src_sel; |
247 | int num_channels; |
248 | const struct lpg_channel_data *channels; |
249 | }; |
250 | |
251 | #define PBS_SW_TRIG_BIT BIT(0) |
252 | |
253 | static int lpg_clear_pbs_trigger(struct lpg *lpg, unsigned int lut_mask) |
254 | { |
255 | u8 val = 0; |
256 | int rc; |
257 | |
258 | if (!lpg->lpg_chan_sdam) |
259 | return 0; |
260 | |
261 | lpg->pbs_en_bitmap &= (~lut_mask); |
262 | if (!lpg->pbs_en_bitmap) { |
263 | rc = nvmem_device_write(nvmem: lpg->lpg_chan_sdam, SDAM_REG_PBS_SEQ_EN, bytes: 1, buf: &val); |
264 | if (rc < 0) |
265 | return rc; |
266 | |
267 | if (lpg->lut_sdam) { |
268 | val = PBS_SW_TRIG_BIT; |
269 | rc = nvmem_device_write(nvmem: lpg->lpg_chan_sdam, SDAM_PBS_TRIG_CLR, bytes: 1, buf: &val); |
270 | if (rc < 0) |
271 | return rc; |
272 | } |
273 | } |
274 | |
275 | return 0; |
276 | } |
277 | |
278 | static int lpg_set_pbs_trigger(struct lpg *lpg, unsigned int lut_mask) |
279 | { |
280 | u8 val = PBS_SW_TRIG_BIT; |
281 | int rc; |
282 | |
283 | if (!lpg->lpg_chan_sdam) |
284 | return 0; |
285 | |
286 | if (!lpg->pbs_en_bitmap) { |
287 | rc = nvmem_device_write(nvmem: lpg->lpg_chan_sdam, SDAM_REG_PBS_SEQ_EN, bytes: 1, buf: &val); |
288 | if (rc < 0) |
289 | return rc; |
290 | |
291 | if (lpg->lut_sdam) { |
292 | rc = nvmem_device_write(nvmem: lpg->lpg_chan_sdam, SDAM_PBS_TRIG_SET, bytes: 1, buf: &val); |
293 | if (rc < 0) |
294 | return rc; |
295 | } else { |
296 | rc = qcom_pbs_trigger_event(pbs: lpg->pbs_dev, bitmap: val); |
297 | if (rc < 0) |
298 | return rc; |
299 | } |
300 | } |
301 | lpg->pbs_en_bitmap |= lut_mask; |
302 | |
303 | return 0; |
304 | } |
305 | |
306 | static int lpg_sdam_configure_triggers(struct lpg_channel *chan, u8 set_trig) |
307 | { |
308 | u32 addr = SDAM_LUT_EN_OFFSET + chan->sdam_offset; |
309 | |
310 | if (!chan->lpg->lpg_chan_sdam) |
311 | return 0; |
312 | |
313 | return nvmem_device_write(nvmem: chan->lpg->lpg_chan_sdam, offset: addr, bytes: 1, buf: &set_trig); |
314 | } |
315 | |
316 | static int triled_set(struct lpg *lpg, unsigned int mask, unsigned int enable) |
317 | { |
318 | /* Skip if we don't have a triled block */ |
319 | if (!lpg->triled_base) |
320 | return 0; |
321 | |
322 | return regmap_update_bits(map: lpg->map, reg: lpg->triled_base + TRI_LED_EN_CTL, |
323 | mask, val: enable); |
324 | } |
325 | |
326 | static int lpg_lut_store_sdam(struct lpg *lpg, struct led_pattern *pattern, |
327 | size_t len, unsigned int *lo_idx, unsigned int *hi_idx) |
328 | { |
329 | unsigned int idx; |
330 | u8 brightness; |
331 | int i, rc; |
332 | u16 addr; |
333 | |
334 | if (len > lpg->lut_size) { |
335 | dev_err(lpg->dev, "Pattern length (%zu) exceeds maximum pattern length (%d)\n", |
336 | len, lpg->lut_size); |
337 | return -EINVAL; |
338 | } |
339 | |
340 | idx = bitmap_find_next_zero_area(map: lpg->lut_bitmap, size: lpg->lut_size, start: 0, nr: len, align_mask: 0); |
341 | if (idx >= lpg->lut_size) |
342 | return -ENOSPC; |
343 | |
344 | for (i = 0; i < len; i++) { |
345 | brightness = pattern[i].brightness; |
346 | |
347 | if (lpg->lut_sdam) { |
348 | addr = SDAM_LUT_SDAM_LUT_PATTERN_OFFSET + i + idx; |
349 | rc = nvmem_device_write(nvmem: lpg->lut_sdam, offset: addr, bytes: 1, buf: &brightness); |
350 | } else { |
351 | addr = SDAM_LPG_SDAM_LUT_PATTERN_OFFSET + i + idx; |
352 | rc = nvmem_device_write(nvmem: lpg->lpg_chan_sdam, offset: addr, bytes: 1, buf: &brightness); |
353 | } |
354 | |
355 | if (rc < 0) |
356 | return rc; |
357 | } |
358 | |
359 | bitmap_set(map: lpg->lut_bitmap, start: idx, nbits: len); |
360 | |
361 | *lo_idx = idx; |
362 | *hi_idx = idx + len - 1; |
363 | |
364 | return 0; |
365 | } |
366 | |
367 | static int lpg_lut_store(struct lpg *lpg, struct led_pattern *pattern, |
368 | size_t len, unsigned int *lo_idx, unsigned int *hi_idx) |
369 | { |
370 | unsigned int idx; |
371 | u16 val; |
372 | int i; |
373 | |
374 | idx = bitmap_find_next_zero_area(map: lpg->lut_bitmap, size: lpg->lut_size, |
375 | start: 0, nr: len, align_mask: 0); |
376 | if (idx >= lpg->lut_size) |
377 | return -ENOMEM; |
378 | |
379 | for (i = 0; i < len; i++) { |
380 | val = pattern[i].brightness; |
381 | |
382 | regmap_bulk_write(map: lpg->map, reg: lpg->lut_base + LPG_LUT_REG(idx + i), |
383 | val: &val, val_count: sizeof(val)); |
384 | } |
385 | |
386 | bitmap_set(map: lpg->lut_bitmap, start: idx, nbits: len); |
387 | |
388 | *lo_idx = idx; |
389 | *hi_idx = idx + len - 1; |
390 | |
391 | return 0; |
392 | } |
393 | |
394 | static void lpg_lut_free(struct lpg *lpg, unsigned int lo_idx, unsigned int hi_idx) |
395 | { |
396 | int len; |
397 | |
398 | len = hi_idx - lo_idx + 1; |
399 | if (len == 1) |
400 | return; |
401 | |
402 | bitmap_clear(map: lpg->lut_bitmap, start: lo_idx, nbits: len); |
403 | } |
404 | |
405 | static int lpg_lut_sync(struct lpg *lpg, unsigned int mask) |
406 | { |
407 | if (!lpg->lut_base) |
408 | return 0; |
409 | |
410 | return regmap_write(map: lpg->map, reg: lpg->lut_base + RAMP_CONTROL_REG, val: mask); |
411 | } |
412 | |
413 | static const unsigned int lpg_clk_rates[] = {0, 1024, 32768, 19200000}; |
414 | static const unsigned int lpg_clk_rates_hi_res[] = {0, 1024, 32768, 19200000, 76800000}; |
415 | static const unsigned int lpg_pre_divs[] = {1, 3, 5, 6}; |
416 | static const unsigned int lpg_pwm_resolution[] = {6, 9}; |
417 | static const unsigned int lpg_pwm_resolution_hi_res[] = {8, 9, 10, 11, 12, 13, 14, 15}; |
418 | |
419 | static int lpg_calc_freq(struct lpg_channel *chan, uint64_t period) |
420 | { |
421 | unsigned int i, pwm_resolution_count, best_pwm_resolution_sel = 0; |
422 | const unsigned int *clk_rate_arr, *pwm_resolution_arr; |
423 | unsigned int clk_sel, clk_len, best_clk = 0; |
424 | unsigned int div, best_div = 0; |
425 | unsigned int m, best_m = 0; |
426 | unsigned int resolution; |
427 | unsigned int error; |
428 | unsigned int best_err = UINT_MAX; |
429 | u64 max_period, min_period; |
430 | u64 best_period = 0; |
431 | u64 max_res; |
432 | |
433 | /* |
434 | * The PWM period is determined by: |
435 | * |
436 | * resolution * pre_div * 2^M |
437 | * period = -------------------------- |
438 | * refclk |
439 | * |
440 | * Resolution = 2^{6 or 9} bits for PWM or |
441 | * 2^{8, 9, 10, 11, 12, 13, 14, 15} bits for high resolution PWM |
442 | * pre_div = {1, 3, 5, 6} and |
443 | * M = [0..7]. |
444 | * |
445 | * This allows for periods between 3uS and 384s for PWM channels and periods between |
446 | * 3uS and 24576s for high resolution PWMs. |
447 | * The PWM framework wants a period of equal or lower length than requested, |
448 | * reject anything below minimum period. |
449 | */ |
450 | |
451 | if (chan->subtype == LPG_SUBTYPE_HI_RES_PWM) { |
452 | clk_rate_arr = lpg_clk_rates_hi_res; |
453 | clk_len = ARRAY_SIZE(lpg_clk_rates_hi_res); |
454 | pwm_resolution_arr = lpg_pwm_resolution_hi_res; |
455 | pwm_resolution_count = ARRAY_SIZE(lpg_pwm_resolution_hi_res); |
456 | max_res = LPG_RESOLUTION_15BIT; |
457 | } else { |
458 | clk_rate_arr = lpg_clk_rates; |
459 | clk_len = ARRAY_SIZE(lpg_clk_rates); |
460 | pwm_resolution_arr = lpg_pwm_resolution; |
461 | pwm_resolution_count = ARRAY_SIZE(lpg_pwm_resolution); |
462 | max_res = LPG_RESOLUTION_9BIT; |
463 | } |
464 | |
465 | min_period = div64_u64(dividend: (u64)NSEC_PER_SEC * ((1 << pwm_resolution_arr[0]) - 1), |
466 | divisor: clk_rate_arr[clk_len - 1]); |
467 | if (period <= min_period) |
468 | return -EINVAL; |
469 | |
470 | /* Limit period to largest possible value, to avoid overflows */ |
471 | max_period = div64_u64(dividend: (u64)NSEC_PER_SEC * max_res * LPG_MAX_PREDIV * (1 << LPG_MAX_M), |
472 | divisor: 1024); |
473 | if (period > max_period) |
474 | period = max_period; |
475 | |
476 | /* |
477 | * Search for the pre_div, refclk, resolution and M by solving the rewritten formula |
478 | * for each refclk, resolution and pre_div value: |
479 | * |
480 | * period * refclk |
481 | * M = log2 ------------------------------------- |
482 | * NSEC_PER_SEC * pre_div * resolution |
483 | */ |
484 | |
485 | for (i = 0; i < pwm_resolution_count; i++) { |
486 | resolution = (1 << pwm_resolution_arr[i]) - 1; |
487 | for (clk_sel = 1; clk_sel < clk_len; clk_sel++) { |
488 | u64 numerator = period * clk_rate_arr[clk_sel]; |
489 | |
490 | for (div = 0; div < ARRAY_SIZE(lpg_pre_divs); div++) { |
491 | u64 denominator = (u64)NSEC_PER_SEC * lpg_pre_divs[div] * |
492 | resolution; |
493 | u64 actual; |
494 | u64 ratio; |
495 | |
496 | if (numerator < denominator) |
497 | continue; |
498 | |
499 | ratio = div64_u64(dividend: numerator, divisor: denominator); |
500 | m = ilog2(ratio); |
501 | if (m > LPG_MAX_M) |
502 | m = LPG_MAX_M; |
503 | |
504 | actual = DIV_ROUND_UP_ULL(denominator * (1 << m), |
505 | clk_rate_arr[clk_sel]); |
506 | error = period - actual; |
507 | if (error < best_err) { |
508 | best_err = error; |
509 | best_div = div; |
510 | best_m = m; |
511 | best_clk = clk_sel; |
512 | best_period = actual; |
513 | best_pwm_resolution_sel = i; |
514 | } |
515 | } |
516 | } |
517 | } |
518 | chan->clk_sel = best_clk; |
519 | chan->pre_div_sel = best_div; |
520 | chan->pre_div_exp = best_m; |
521 | chan->period = best_period; |
522 | chan->pwm_resolution_sel = best_pwm_resolution_sel; |
523 | return 0; |
524 | } |
525 | |
526 | static void lpg_calc_duty(struct lpg_channel *chan, uint64_t duty) |
527 | { |
528 | unsigned int max; |
529 | unsigned int val; |
530 | unsigned int clk_rate; |
531 | |
532 | if (chan->subtype == LPG_SUBTYPE_HI_RES_PWM) { |
533 | max = BIT(lpg_pwm_resolution_hi_res[chan->pwm_resolution_sel]) - 1; |
534 | clk_rate = lpg_clk_rates_hi_res[chan->clk_sel]; |
535 | } else { |
536 | max = BIT(lpg_pwm_resolution[chan->pwm_resolution_sel]) - 1; |
537 | clk_rate = lpg_clk_rates[chan->clk_sel]; |
538 | } |
539 | |
540 | val = div64_u64(dividend: duty * clk_rate, |
541 | divisor: (u64)NSEC_PER_SEC * lpg_pre_divs[chan->pre_div_sel] * (1 << chan->pre_div_exp)); |
542 | |
543 | chan->pwm_value = min(val, max); |
544 | } |
545 | |
546 | static void lpg_apply_freq(struct lpg_channel *chan) |
547 | { |
548 | unsigned long val; |
549 | struct lpg *lpg = chan->lpg; |
550 | |
551 | if (!chan->enabled) |
552 | return; |
553 | |
554 | val = chan->clk_sel; |
555 | |
556 | /* Specify resolution, based on the subtype of the channel */ |
557 | switch (chan->subtype) { |
558 | case LPG_SUBTYPE_LPG: |
559 | val |= GENMASK(5, 4); |
560 | break; |
561 | case LPG_SUBTYPE_PWM: |
562 | val |= FIELD_PREP(PWM_SIZE_SELECT_MASK, chan->pwm_resolution_sel); |
563 | break; |
564 | case LPG_SUBTYPE_HI_RES_PWM: |
565 | val |= FIELD_PREP(PWM_SIZE_HI_RES_MASK, chan->pwm_resolution_sel); |
566 | break; |
567 | case LPG_SUBTYPE_LPG_LITE: |
568 | default: |
569 | val |= BIT(4); |
570 | break; |
571 | } |
572 | |
573 | regmap_write(map: lpg->map, reg: chan->base + LPG_SIZE_CLK_REG, val); |
574 | |
575 | val = FIELD_PREP(PWM_FREQ_PRE_DIV_MASK, chan->pre_div_sel) | |
576 | FIELD_PREP(PWM_FREQ_EXP_MASK, chan->pre_div_exp); |
577 | regmap_write(map: lpg->map, reg: chan->base + LPG_PREDIV_CLK_REG, val); |
578 | } |
579 | |
580 | #define LPG_ENABLE_GLITCH_REMOVAL BIT(5) |
581 | |
582 | static void lpg_enable_glitch(struct lpg_channel *chan) |
583 | { |
584 | struct lpg *lpg = chan->lpg; |
585 | |
586 | regmap_update_bits(map: lpg->map, reg: chan->base + PWM_TYPE_CONFIG_REG, |
587 | LPG_ENABLE_GLITCH_REMOVAL, val: 0); |
588 | } |
589 | |
590 | static void lpg_disable_glitch(struct lpg_channel *chan) |
591 | { |
592 | struct lpg *lpg = chan->lpg; |
593 | |
594 | regmap_update_bits(map: lpg->map, reg: chan->base + PWM_TYPE_CONFIG_REG, |
595 | LPG_ENABLE_GLITCH_REMOVAL, |
596 | LPG_ENABLE_GLITCH_REMOVAL); |
597 | } |
598 | |
599 | static void lpg_apply_pwm_value(struct lpg_channel *chan) |
600 | { |
601 | struct lpg *lpg = chan->lpg; |
602 | u16 val = chan->pwm_value; |
603 | |
604 | if (!chan->enabled) |
605 | return; |
606 | |
607 | regmap_bulk_write(map: lpg->map, reg: chan->base + PWM_VALUE_REG, val: &val, val_count: sizeof(val)); |
608 | } |
609 | |
610 | #define LPG_PATTERN_CONFIG_LO_TO_HI BIT(4) |
611 | #define LPG_PATTERN_CONFIG_REPEAT BIT(3) |
612 | #define LPG_PATTERN_CONFIG_TOGGLE BIT(2) |
613 | #define LPG_PATTERN_CONFIG_PAUSE_HI BIT(1) |
614 | #define LPG_PATTERN_CONFIG_PAUSE_LO BIT(0) |
615 | |
616 | static void lpg_sdam_apply_lut_control(struct lpg_channel *chan) |
617 | { |
618 | struct nvmem_device *lpg_chan_sdam = chan->lpg->lpg_chan_sdam; |
619 | unsigned int lo_idx = chan->pattern_lo_idx; |
620 | unsigned int hi_idx = chan->pattern_hi_idx; |
621 | u8 val = 0, conf = 0, lut_offset = 0; |
622 | unsigned int hi_pause, lo_pause; |
623 | struct lpg *lpg = chan->lpg; |
624 | |
625 | if (!chan->ramp_enabled || chan->pattern_lo_idx == chan->pattern_hi_idx) |
626 | return; |
627 | |
628 | hi_pause = DIV_ROUND_UP(chan->ramp_hi_pause_ms, chan->ramp_tick_ms); |
629 | lo_pause = DIV_ROUND_UP(chan->ramp_lo_pause_ms, chan->ramp_tick_ms); |
630 | |
631 | if (!chan->ramp_oneshot) |
632 | conf |= LPG_PATTERN_CONFIG_REPEAT; |
633 | if (chan->ramp_hi_pause_ms && lpg->lut_sdam) |
634 | conf |= LPG_PATTERN_CONFIG_PAUSE_HI; |
635 | if (chan->ramp_lo_pause_ms && lpg->lut_sdam) |
636 | conf |= LPG_PATTERN_CONFIG_PAUSE_LO; |
637 | |
638 | if (lpg->lut_sdam) { |
639 | lut_offset = SDAM_LUT_SDAM_LUT_PATTERN_OFFSET - SDAM_START_BASE; |
640 | hi_idx += lut_offset; |
641 | lo_idx += lut_offset; |
642 | } |
643 | |
644 | nvmem_device_write(nvmem: lpg_chan_sdam, SDAM_PBS_SCRATCH_LUT_COUNTER_OFFSET + chan->sdam_offset, bytes: 1, buf: &val); |
645 | nvmem_device_write(nvmem: lpg_chan_sdam, SDAM_PATTERN_CONFIG_OFFSET + chan->sdam_offset, bytes: 1, buf: &conf); |
646 | nvmem_device_write(nvmem: lpg_chan_sdam, SDAM_END_INDEX_OFFSET + chan->sdam_offset, bytes: 1, buf: &hi_idx); |
647 | nvmem_device_write(nvmem: lpg_chan_sdam, SDAM_START_INDEX_OFFSET + chan->sdam_offset, bytes: 1, buf: &lo_idx); |
648 | |
649 | val = RAMP_STEP_DURATION(chan->ramp_tick_ms); |
650 | nvmem_device_write(nvmem: lpg_chan_sdam, SDAM_REG_RAMP_STEP_DURATION, bytes: 1, buf: &val); |
651 | |
652 | if (lpg->lut_sdam) { |
653 | nvmem_device_write(nvmem: lpg_chan_sdam, SDAM_PAUSE_HI_MULTIPLIER_OFFSET + chan->sdam_offset, bytes: 1, buf: &hi_pause); |
654 | nvmem_device_write(nvmem: lpg_chan_sdam, SDAM_PAUSE_LO_MULTIPLIER_OFFSET + chan->sdam_offset, bytes: 1, buf: &lo_pause); |
655 | } |
656 | |
657 | } |
658 | |
659 | static void lpg_apply_lut_control(struct lpg_channel *chan) |
660 | { |
661 | struct lpg *lpg = chan->lpg; |
662 | unsigned int hi_pause; |
663 | unsigned int lo_pause; |
664 | unsigned int conf = 0; |
665 | unsigned int lo_idx = chan->pattern_lo_idx; |
666 | unsigned int hi_idx = chan->pattern_hi_idx; |
667 | u16 step = chan->ramp_tick_ms; |
668 | |
669 | if (!chan->ramp_enabled || chan->pattern_lo_idx == chan->pattern_hi_idx) |
670 | return; |
671 | |
672 | hi_pause = DIV_ROUND_UP(chan->ramp_hi_pause_ms, step); |
673 | lo_pause = DIV_ROUND_UP(chan->ramp_lo_pause_ms, step); |
674 | |
675 | if (!chan->ramp_reverse) |
676 | conf |= LPG_PATTERN_CONFIG_LO_TO_HI; |
677 | if (!chan->ramp_oneshot) |
678 | conf |= LPG_PATTERN_CONFIG_REPEAT; |
679 | if (chan->ramp_ping_pong) |
680 | conf |= LPG_PATTERN_CONFIG_TOGGLE; |
681 | if (chan->ramp_hi_pause_ms) |
682 | conf |= LPG_PATTERN_CONFIG_PAUSE_HI; |
683 | if (chan->ramp_lo_pause_ms) |
684 | conf |= LPG_PATTERN_CONFIG_PAUSE_LO; |
685 | |
686 | regmap_write(map: lpg->map, reg: chan->base + LPG_PATTERN_CONFIG_REG, val: conf); |
687 | regmap_write(map: lpg->map, reg: chan->base + LPG_HI_IDX_REG, val: hi_idx); |
688 | regmap_write(map: lpg->map, reg: chan->base + LPG_LO_IDX_REG, val: lo_idx); |
689 | |
690 | regmap_bulk_write(map: lpg->map, reg: chan->base + LPG_RAMP_DURATION_REG, val: &step, val_count: sizeof(step)); |
691 | regmap_write(map: lpg->map, reg: chan->base + LPG_HI_PAUSE_REG, val: hi_pause); |
692 | regmap_write(map: lpg->map, reg: chan->base + LPG_LO_PAUSE_REG, val: lo_pause); |
693 | } |
694 | |
695 | #define LPG_ENABLE_CONTROL_OUTPUT BIT(7) |
696 | #define LPG_ENABLE_CONTROL_BUFFER_TRISTATE BIT(5) |
697 | #define LPG_ENABLE_CONTROL_SRC_PWM BIT(2) |
698 | #define LPG_ENABLE_CONTROL_RAMP_GEN BIT(1) |
699 | |
700 | static void lpg_apply_control(struct lpg_channel *chan) |
701 | { |
702 | unsigned int ctrl; |
703 | struct lpg *lpg = chan->lpg; |
704 | |
705 | ctrl = LPG_ENABLE_CONTROL_BUFFER_TRISTATE; |
706 | |
707 | if (chan->enabled) |
708 | ctrl |= LPG_ENABLE_CONTROL_OUTPUT; |
709 | |
710 | if (chan->pattern_lo_idx != chan->pattern_hi_idx) |
711 | ctrl |= LPG_ENABLE_CONTROL_RAMP_GEN; |
712 | else |
713 | ctrl |= LPG_ENABLE_CONTROL_SRC_PWM; |
714 | |
715 | regmap_write(map: lpg->map, reg: chan->base + PWM_ENABLE_CONTROL_REG, val: ctrl); |
716 | |
717 | /* |
718 | * Due to LPG hardware bug, in the PWM mode, having enabled PWM, |
719 | * We have to write PWM values one more time. |
720 | */ |
721 | if (chan->enabled) |
722 | lpg_apply_pwm_value(chan); |
723 | } |
724 | |
725 | #define LPG_SYNC_PWM BIT(0) |
726 | |
727 | static void lpg_apply_sync(struct lpg_channel *chan) |
728 | { |
729 | struct lpg *lpg = chan->lpg; |
730 | |
731 | regmap_write(map: lpg->map, reg: chan->base + PWM_SYNC_REG, LPG_SYNC_PWM); |
732 | } |
733 | |
734 | static int lpg_parse_dtest(struct lpg *lpg) |
735 | { |
736 | struct lpg_channel *chan; |
737 | struct device_node *np = lpg->dev->of_node; |
738 | int count; |
739 | int ret; |
740 | int i; |
741 | |
742 | count = of_property_count_u32_elems(np, propname: "qcom,dtest"); |
743 | if (count == -EINVAL) { |
744 | return 0; |
745 | } else if (count < 0) { |
746 | ret = count; |
747 | goto err_malformed; |
748 | } else if (count != lpg->data->num_channels * 2) { |
749 | return dev_err_probe(dev: lpg->dev, err: -EINVAL, |
750 | fmt: "qcom,dtest needs to be %d items\n", |
751 | lpg->data->num_channels * 2); |
752 | } |
753 | |
754 | for (i = 0; i < lpg->data->num_channels; i++) { |
755 | chan = &lpg->channels[i]; |
756 | |
757 | ret = of_property_read_u32_index(np, propname: "qcom,dtest", index: i * 2, |
758 | out_value: &chan->dtest_line); |
759 | if (ret) |
760 | goto err_malformed; |
761 | |
762 | ret = of_property_read_u32_index(np, propname: "qcom,dtest", index: i * 2 + 1, |
763 | out_value: &chan->dtest_value); |
764 | if (ret) |
765 | goto err_malformed; |
766 | } |
767 | |
768 | return 0; |
769 | |
770 | err_malformed: |
771 | return dev_err_probe(dev: lpg->dev, err: ret, fmt: "malformed qcom,dtest\n"); |
772 | } |
773 | |
774 | static void lpg_apply_dtest(struct lpg_channel *chan) |
775 | { |
776 | struct lpg *lpg = chan->lpg; |
777 | |
778 | if (!chan->dtest_line) |
779 | return; |
780 | |
781 | regmap_write(map: lpg->map, reg: chan->base + PWM_SEC_ACCESS_REG, val: 0xa5); |
782 | regmap_write(map: lpg->map, reg: chan->base + PWM_DTEST_REG(chan->dtest_line), |
783 | val: chan->dtest_value); |
784 | } |
785 | |
786 | static void lpg_apply(struct lpg_channel *chan) |
787 | { |
788 | lpg_disable_glitch(chan); |
789 | lpg_apply_freq(chan); |
790 | lpg_apply_pwm_value(chan); |
791 | lpg_apply_control(chan); |
792 | lpg_apply_sync(chan); |
793 | if (chan->lpg->lpg_chan_sdam) |
794 | lpg_sdam_apply_lut_control(chan); |
795 | else |
796 | lpg_apply_lut_control(chan); |
797 | lpg_enable_glitch(chan); |
798 | } |
799 | |
800 | static void lpg_brightness_set(struct lpg_led *led, struct led_classdev *cdev, |
801 | struct mc_subled *subleds) |
802 | { |
803 | enum led_brightness brightness; |
804 | struct lpg_channel *chan; |
805 | unsigned int triled_enabled = 0; |
806 | unsigned int triled_mask = 0; |
807 | unsigned int lut_mask = 0; |
808 | unsigned int duty; |
809 | struct lpg *lpg = led->lpg; |
810 | int i; |
811 | |
812 | for (i = 0; i < led->num_channels; i++) { |
813 | chan = led->channels[i]; |
814 | brightness = subleds[i].brightness; |
815 | |
816 | if (brightness == LED_OFF) { |
817 | chan->enabled = false; |
818 | chan->ramp_enabled = false; |
819 | } else if (chan->pattern_lo_idx != chan->pattern_hi_idx) { |
820 | lpg_calc_freq(chan, NSEC_PER_MSEC); |
821 | lpg_sdam_configure_triggers(chan, set_trig: 1); |
822 | |
823 | chan->enabled = true; |
824 | chan->ramp_enabled = true; |
825 | |
826 | lut_mask |= chan->lut_mask; |
827 | triled_enabled |= chan->triled_mask; |
828 | } else { |
829 | lpg_calc_freq(chan, NSEC_PER_MSEC); |
830 | |
831 | duty = div_u64(dividend: brightness * chan->period, divisor: cdev->max_brightness); |
832 | lpg_calc_duty(chan, duty); |
833 | chan->enabled = true; |
834 | chan->ramp_enabled = false; |
835 | |
836 | triled_enabled |= chan->triled_mask; |
837 | } |
838 | |
839 | triled_mask |= chan->triled_mask; |
840 | |
841 | lpg_apply(chan); |
842 | } |
843 | |
844 | /* Toggle triled lines */ |
845 | if (triled_mask) |
846 | triled_set(lpg, mask: triled_mask, enable: triled_enabled); |
847 | |
848 | /* Trigger start of ramp generator(s) */ |
849 | if (lut_mask) { |
850 | lpg_lut_sync(lpg, mask: lut_mask); |
851 | lpg_set_pbs_trigger(lpg, lut_mask); |
852 | } |
853 | } |
854 | |
855 | static int lpg_brightness_single_set(struct led_classdev *cdev, |
856 | enum led_brightness value) |
857 | { |
858 | struct lpg_led *led = container_of(cdev, struct lpg_led, cdev); |
859 | struct mc_subled info; |
860 | |
861 | mutex_lock(&led->lpg->lock); |
862 | |
863 | info.brightness = value; |
864 | lpg_brightness_set(led, cdev, subleds: &info); |
865 | |
866 | mutex_unlock(lock: &led->lpg->lock); |
867 | |
868 | return 0; |
869 | } |
870 | |
871 | static int lpg_brightness_mc_set(struct led_classdev *cdev, |
872 | enum led_brightness value) |
873 | { |
874 | struct led_classdev_mc *mc = lcdev_to_mccdev(led_cdev: cdev); |
875 | struct lpg_led *led = container_of(mc, struct lpg_led, mcdev); |
876 | |
877 | mutex_lock(&led->lpg->lock); |
878 | |
879 | led_mc_calc_color_components(mcled_cdev: mc, brightness: value); |
880 | lpg_brightness_set(led, cdev, subleds: mc->subled_info); |
881 | |
882 | mutex_unlock(lock: &led->lpg->lock); |
883 | |
884 | return 0; |
885 | } |
886 | |
887 | static int lpg_blink_set(struct lpg_led *led, |
888 | unsigned long *delay_on, unsigned long *delay_off) |
889 | { |
890 | struct lpg_channel *chan; |
891 | unsigned int period; |
892 | unsigned int triled_mask = 0; |
893 | struct lpg *lpg = led->lpg; |
894 | u64 duty; |
895 | int i; |
896 | |
897 | if (!*delay_on && !*delay_off) { |
898 | *delay_on = 500; |
899 | *delay_off = 500; |
900 | } |
901 | |
902 | duty = *delay_on * NSEC_PER_MSEC; |
903 | period = (*delay_on + *delay_off) * NSEC_PER_MSEC; |
904 | |
905 | for (i = 0; i < led->num_channels; i++) { |
906 | chan = led->channels[i]; |
907 | |
908 | lpg_calc_freq(chan, period); |
909 | lpg_calc_duty(chan, duty); |
910 | |
911 | chan->enabled = true; |
912 | chan->ramp_enabled = false; |
913 | |
914 | triled_mask |= chan->triled_mask; |
915 | |
916 | lpg_apply(chan); |
917 | } |
918 | |
919 | /* Enable triled lines */ |
920 | triled_set(lpg, mask: triled_mask, enable: triled_mask); |
921 | |
922 | chan = led->channels[0]; |
923 | duty = div_u64(dividend: chan->pwm_value * chan->period, LPG_RESOLUTION_9BIT); |
924 | *delay_on = div_u64(dividend: duty, NSEC_PER_MSEC); |
925 | *delay_off = div_u64(dividend: chan->period - duty, NSEC_PER_MSEC); |
926 | |
927 | return 0; |
928 | } |
929 | |
930 | static int lpg_blink_single_set(struct led_classdev *cdev, |
931 | unsigned long *delay_on, unsigned long *delay_off) |
932 | { |
933 | struct lpg_led *led = container_of(cdev, struct lpg_led, cdev); |
934 | int ret; |
935 | |
936 | mutex_lock(&led->lpg->lock); |
937 | |
938 | ret = lpg_blink_set(led, delay_on, delay_off); |
939 | |
940 | mutex_unlock(lock: &led->lpg->lock); |
941 | |
942 | return ret; |
943 | } |
944 | |
945 | static int lpg_blink_mc_set(struct led_classdev *cdev, |
946 | unsigned long *delay_on, unsigned long *delay_off) |
947 | { |
948 | struct led_classdev_mc *mc = lcdev_to_mccdev(led_cdev: cdev); |
949 | struct lpg_led *led = container_of(mc, struct lpg_led, mcdev); |
950 | int ret; |
951 | |
952 | mutex_lock(&led->lpg->lock); |
953 | |
954 | ret = lpg_blink_set(led, delay_on, delay_off); |
955 | |
956 | mutex_unlock(lock: &led->lpg->lock); |
957 | |
958 | return ret; |
959 | } |
960 | |
961 | static int lpg_pattern_set(struct lpg_led *led, struct led_pattern *led_pattern, |
962 | u32 len, int repeat) |
963 | { |
964 | struct lpg_channel *chan; |
965 | struct lpg *lpg = led->lpg; |
966 | struct led_pattern *pattern; |
967 | unsigned int brightness_a; |
968 | unsigned int brightness_b; |
969 | unsigned int hi_pause = 0; |
970 | unsigned int lo_pause = 0; |
971 | unsigned int actual_len; |
972 | unsigned int delta_t; |
973 | unsigned int lo_idx; |
974 | unsigned int hi_idx; |
975 | unsigned int i; |
976 | bool ping_pong = true; |
977 | int ret = -EINVAL; |
978 | |
979 | /* Hardware only support oneshot or indefinite loops */ |
980 | if (repeat != -1 && repeat != 1) |
981 | return -EINVAL; |
982 | |
983 | /* |
984 | * The standardized leds-trigger-pattern format defines that the |
985 | * brightness of the LED follows a linear transition from one entry |
986 | * in the pattern to the next, over the given delta_t time. It |
987 | * describes that the way to perform instant transitions a zero-length |
988 | * entry should be added following a pattern entry. |
989 | * |
990 | * The LPG hardware is only able to perform the latter (no linear |
991 | * transitions), so require each entry in the pattern to be followed by |
992 | * a zero-length transition. |
993 | */ |
994 | if (len % 2) |
995 | return -EINVAL; |
996 | |
997 | pattern = kcalloc(len / 2, sizeof(*pattern), GFP_KERNEL); |
998 | if (!pattern) |
999 | return -ENOMEM; |
1000 | |
1001 | for (i = 0; i < len; i += 2) { |
1002 | if (led_pattern[i].brightness != led_pattern[i + 1].brightness) |
1003 | goto out_free_pattern; |
1004 | if (led_pattern[i + 1].delta_t != 0) |
1005 | goto out_free_pattern; |
1006 | |
1007 | pattern[i / 2].brightness = led_pattern[i].brightness; |
1008 | pattern[i / 2].delta_t = led_pattern[i].delta_t; |
1009 | } |
1010 | |
1011 | len /= 2; |
1012 | |
1013 | /* |
1014 | * Specifying a pattern of length 1 causes the hardware to iterate |
1015 | * through the entire LUT, so prohibit this. |
1016 | */ |
1017 | if (len < 2) |
1018 | goto out_free_pattern; |
1019 | |
1020 | /* |
1021 | * The LPG plays patterns with at a fixed pace, a "low pause" can be |
1022 | * used to stretch the first delay of the pattern and a "high pause" |
1023 | * the last one. |
1024 | * |
1025 | * In order to save space the pattern can be played in "ping pong" |
1026 | * mode, in which the pattern is first played forward, then "high |
1027 | * pause" is applied, then the pattern is played backwards and finally |
1028 | * the "low pause" is applied. |
1029 | * |
1030 | * The middle elements of the pattern are used to determine delta_t and |
1031 | * the "low pause" and "high pause" multipliers are derrived from this. |
1032 | * |
1033 | * The first element in the pattern is used to determine "low pause". |
1034 | * |
1035 | * If the specified pattern is a palindrome the ping pong mode is |
1036 | * enabled. In this scenario the delta_t of the middle entry (i.e. the |
1037 | * last in the programmed pattern) determines the "high pause". |
1038 | * |
1039 | * SDAM-based devices do not support "ping pong", and only supports |
1040 | * "low pause" and "high pause" with a dedicated SDAM LUT. |
1041 | */ |
1042 | |
1043 | /* Detect palindromes and use "ping pong" to reduce LUT usage */ |
1044 | if (lpg->lut_base) { |
1045 | for (i = 0; i < len / 2; i++) { |
1046 | brightness_a = pattern[i].brightness; |
1047 | brightness_b = pattern[len - i - 1].brightness; |
1048 | |
1049 | if (brightness_a != brightness_b) { |
1050 | ping_pong = false; |
1051 | break; |
1052 | } |
1053 | } |
1054 | } else |
1055 | ping_pong = false; |
1056 | |
1057 | /* The pattern length to be written to the LUT */ |
1058 | if (ping_pong) |
1059 | actual_len = (len + 1) / 2; |
1060 | else |
1061 | actual_len = len; |
1062 | |
1063 | /* |
1064 | * Validate that all delta_t in the pattern are the same, with the |
1065 | * exception of the middle element in case of ping_pong. |
1066 | */ |
1067 | delta_t = pattern[1].delta_t; |
1068 | for (i = 2; i < len; i++) { |
1069 | if (pattern[i].delta_t != delta_t) { |
1070 | /* |
1071 | * Allow last entry in the full or shortened pattern to |
1072 | * specify hi pause. Reject other variations. |
1073 | */ |
1074 | if (i != actual_len - 1) |
1075 | goto out_free_pattern; |
1076 | } |
1077 | } |
1078 | |
1079 | /* LPG_RAMP_DURATION_REG is a 9bit */ |
1080 | if (delta_t >= BIT(9)) |
1081 | goto out_free_pattern; |
1082 | |
1083 | /* |
1084 | * Find "low pause" and "high pause" in the pattern in the LUT case. |
1085 | * SDAM-based devices without dedicated LUT SDAM require equal |
1086 | * duration of all steps. |
1087 | */ |
1088 | if (lpg->lut_base || lpg->lut_sdam) { |
1089 | lo_pause = pattern[0].delta_t; |
1090 | hi_pause = pattern[actual_len - 1].delta_t; |
1091 | } else { |
1092 | if (delta_t != pattern[0].delta_t || delta_t != pattern[actual_len - 1].delta_t) |
1093 | goto out_free_pattern; |
1094 | } |
1095 | |
1096 | |
1097 | mutex_lock(&lpg->lock); |
1098 | |
1099 | if (lpg->lut_base) |
1100 | ret = lpg_lut_store(lpg, pattern, len: actual_len, lo_idx: &lo_idx, hi_idx: &hi_idx); |
1101 | else |
1102 | ret = lpg_lut_store_sdam(lpg, pattern, len: actual_len, lo_idx: &lo_idx, hi_idx: &hi_idx); |
1103 | |
1104 | if (ret < 0) |
1105 | goto out_unlock; |
1106 | |
1107 | for (i = 0; i < led->num_channels; i++) { |
1108 | chan = led->channels[i]; |
1109 | |
1110 | chan->ramp_tick_ms = delta_t; |
1111 | chan->ramp_ping_pong = ping_pong; |
1112 | chan->ramp_oneshot = repeat != -1; |
1113 | |
1114 | chan->ramp_lo_pause_ms = lo_pause; |
1115 | chan->ramp_hi_pause_ms = hi_pause; |
1116 | |
1117 | chan->pattern_lo_idx = lo_idx; |
1118 | chan->pattern_hi_idx = hi_idx; |
1119 | } |
1120 | |
1121 | out_unlock: |
1122 | mutex_unlock(lock: &lpg->lock); |
1123 | out_free_pattern: |
1124 | kfree(objp: pattern); |
1125 | |
1126 | return ret; |
1127 | } |
1128 | |
1129 | static int lpg_pattern_single_set(struct led_classdev *cdev, |
1130 | struct led_pattern *pattern, u32 len, |
1131 | int repeat) |
1132 | { |
1133 | struct lpg_led *led = container_of(cdev, struct lpg_led, cdev); |
1134 | int ret; |
1135 | |
1136 | ret = lpg_pattern_set(led, led_pattern: pattern, len, repeat); |
1137 | if (ret < 0) |
1138 | return ret; |
1139 | |
1140 | lpg_brightness_single_set(cdev, value: LED_FULL); |
1141 | |
1142 | return 0; |
1143 | } |
1144 | |
1145 | static int lpg_pattern_mc_set(struct led_classdev *cdev, |
1146 | struct led_pattern *pattern, u32 len, |
1147 | int repeat) |
1148 | { |
1149 | struct led_classdev_mc *mc = lcdev_to_mccdev(led_cdev: cdev); |
1150 | struct lpg_led *led = container_of(mc, struct lpg_led, mcdev); |
1151 | unsigned int triled_mask = 0; |
1152 | int ret, i; |
1153 | |
1154 | for (i = 0; i < led->num_channels; i++) |
1155 | triled_mask |= led->channels[i]->triled_mask; |
1156 | triled_set(lpg: led->lpg, mask: triled_mask, enable: 0); |
1157 | |
1158 | ret = lpg_pattern_set(led, led_pattern: pattern, len, repeat); |
1159 | if (ret < 0) |
1160 | return ret; |
1161 | |
1162 | led_mc_calc_color_components(mcled_cdev: mc, brightness: LED_FULL); |
1163 | lpg_brightness_set(led, cdev, subleds: mc->subled_info); |
1164 | |
1165 | return 0; |
1166 | } |
1167 | |
1168 | static int lpg_pattern_clear(struct lpg_led *led) |
1169 | { |
1170 | struct lpg_channel *chan; |
1171 | struct lpg *lpg = led->lpg; |
1172 | int i; |
1173 | |
1174 | mutex_lock(&lpg->lock); |
1175 | |
1176 | chan = led->channels[0]; |
1177 | lpg_lut_free(lpg, lo_idx: chan->pattern_lo_idx, hi_idx: chan->pattern_hi_idx); |
1178 | |
1179 | for (i = 0; i < led->num_channels; i++) { |
1180 | chan = led->channels[i]; |
1181 | lpg_sdam_configure_triggers(chan, set_trig: 0); |
1182 | lpg_clear_pbs_trigger(lpg: chan->lpg, lut_mask: chan->lut_mask); |
1183 | chan->pattern_lo_idx = 0; |
1184 | chan->pattern_hi_idx = 0; |
1185 | } |
1186 | |
1187 | mutex_unlock(lock: &lpg->lock); |
1188 | |
1189 | return 0; |
1190 | } |
1191 | |
1192 | static int lpg_pattern_single_clear(struct led_classdev *cdev) |
1193 | { |
1194 | struct lpg_led *led = container_of(cdev, struct lpg_led, cdev); |
1195 | |
1196 | return lpg_pattern_clear(led); |
1197 | } |
1198 | |
1199 | static int lpg_pattern_mc_clear(struct led_classdev *cdev) |
1200 | { |
1201 | struct led_classdev_mc *mc = lcdev_to_mccdev(led_cdev: cdev); |
1202 | struct lpg_led *led = container_of(mc, struct lpg_led, mcdev); |
1203 | |
1204 | return lpg_pattern_clear(led); |
1205 | } |
1206 | |
1207 | static inline struct lpg *lpg_pwm_from_chip(struct pwm_chip *chip) |
1208 | { |
1209 | return pwmchip_get_drvdata(chip); |
1210 | } |
1211 | |
1212 | static int lpg_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) |
1213 | { |
1214 | struct lpg *lpg = lpg_pwm_from_chip(chip); |
1215 | struct lpg_channel *chan = &lpg->channels[pwm->hwpwm]; |
1216 | |
1217 | return chan->in_use ? -EBUSY : 0; |
1218 | } |
1219 | |
1220 | /* |
1221 | * Limitations: |
1222 | * - Updating both duty and period is not done atomically, so the output signal |
1223 | * will momentarily be a mix of the settings. |
1224 | * - Changed parameters takes effect immediately. |
1225 | * - A disabled channel outputs a logical 0. |
1226 | */ |
1227 | static int lpg_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, |
1228 | const struct pwm_state *state) |
1229 | { |
1230 | struct lpg *lpg = lpg_pwm_from_chip(chip); |
1231 | struct lpg_channel *chan = &lpg->channels[pwm->hwpwm]; |
1232 | int ret = 0; |
1233 | |
1234 | if (state->polarity != PWM_POLARITY_NORMAL) |
1235 | return -EINVAL; |
1236 | |
1237 | mutex_lock(&lpg->lock); |
1238 | |
1239 | if (state->enabled) { |
1240 | ret = lpg_calc_freq(chan, period: state->period); |
1241 | if (ret < 0) |
1242 | goto out_unlock; |
1243 | |
1244 | lpg_calc_duty(chan, duty: state->duty_cycle); |
1245 | } |
1246 | chan->enabled = state->enabled; |
1247 | |
1248 | lpg_apply(chan); |
1249 | |
1250 | triled_set(lpg, mask: chan->triled_mask, enable: chan->enabled ? chan->triled_mask : 0); |
1251 | |
1252 | out_unlock: |
1253 | mutex_unlock(lock: &lpg->lock); |
1254 | |
1255 | return ret; |
1256 | } |
1257 | |
1258 | static int lpg_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, |
1259 | struct pwm_state *state) |
1260 | { |
1261 | struct lpg *lpg = lpg_pwm_from_chip(chip); |
1262 | struct lpg_channel *chan = &lpg->channels[pwm->hwpwm]; |
1263 | unsigned int resolution; |
1264 | unsigned int pre_div; |
1265 | unsigned int refclk; |
1266 | unsigned int val; |
1267 | unsigned int m; |
1268 | u16 pwm_value; |
1269 | int ret; |
1270 | |
1271 | ret = regmap_read(map: lpg->map, reg: chan->base + LPG_SIZE_CLK_REG, val: &val); |
1272 | if (ret) |
1273 | return ret; |
1274 | |
1275 | if (chan->subtype == LPG_SUBTYPE_HI_RES_PWM) { |
1276 | refclk = lpg_clk_rates_hi_res[FIELD_GET(PWM_CLK_SELECT_HI_RES_MASK, val)]; |
1277 | resolution = lpg_pwm_resolution_hi_res[FIELD_GET(PWM_SIZE_HI_RES_MASK, val)]; |
1278 | } else { |
1279 | refclk = lpg_clk_rates[FIELD_GET(PWM_CLK_SELECT_MASK, val)]; |
1280 | resolution = lpg_pwm_resolution[FIELD_GET(PWM_SIZE_SELECT_MASK, val)]; |
1281 | } |
1282 | |
1283 | if (refclk) { |
1284 | ret = regmap_read(map: lpg->map, reg: chan->base + LPG_PREDIV_CLK_REG, val: &val); |
1285 | if (ret) |
1286 | return ret; |
1287 | |
1288 | pre_div = lpg_pre_divs[FIELD_GET(PWM_FREQ_PRE_DIV_MASK, val)]; |
1289 | m = FIELD_GET(PWM_FREQ_EXP_MASK, val); |
1290 | |
1291 | ret = regmap_bulk_read(map: lpg->map, reg: chan->base + PWM_VALUE_REG, val: &pwm_value, val_count: sizeof(pwm_value)); |
1292 | if (ret) |
1293 | return ret; |
1294 | |
1295 | state->period = DIV_ROUND_UP_ULL((u64)NSEC_PER_SEC * ((1 << resolution) - 1) * |
1296 | pre_div * (1 << m), refclk); |
1297 | state->duty_cycle = DIV_ROUND_UP_ULL((u64)NSEC_PER_SEC * pwm_value * pre_div * (1 << m), refclk); |
1298 | } else { |
1299 | state->period = 0; |
1300 | state->duty_cycle = 0; |
1301 | } |
1302 | |
1303 | ret = regmap_read(map: lpg->map, reg: chan->base + PWM_ENABLE_CONTROL_REG, val: &val); |
1304 | if (ret) |
1305 | return ret; |
1306 | |
1307 | state->enabled = FIELD_GET(LPG_ENABLE_CONTROL_OUTPUT, val); |
1308 | state->polarity = PWM_POLARITY_NORMAL; |
1309 | |
1310 | if (state->duty_cycle > state->period) |
1311 | state->duty_cycle = state->period; |
1312 | |
1313 | return 0; |
1314 | } |
1315 | |
1316 | static const struct pwm_ops lpg_pwm_ops = { |
1317 | .request = lpg_pwm_request, |
1318 | .apply = lpg_pwm_apply, |
1319 | .get_state = lpg_pwm_get_state, |
1320 | }; |
1321 | |
1322 | static int lpg_add_pwm(struct lpg *lpg) |
1323 | { |
1324 | struct pwm_chip *chip; |
1325 | int ret; |
1326 | |
1327 | lpg->pwm = chip = devm_pwmchip_alloc(parent: lpg->dev, npwm: lpg->num_channels, sizeof_priv: 0); |
1328 | if (IS_ERR(ptr: chip)) |
1329 | return PTR_ERR(ptr: chip); |
1330 | |
1331 | chip->ops = &lpg_pwm_ops; |
1332 | pwmchip_set_drvdata(chip, data: lpg); |
1333 | |
1334 | ret = devm_pwmchip_add(lpg->dev, chip); |
1335 | if (ret) |
1336 | dev_err_probe(dev: lpg->dev, err: ret, fmt: "failed to add PWM chip\n"); |
1337 | |
1338 | return ret; |
1339 | } |
1340 | |
1341 | static int lpg_parse_channel(struct lpg *lpg, struct device_node *np, |
1342 | struct lpg_channel **channel) |
1343 | { |
1344 | struct lpg_channel *chan; |
1345 | u32 color = LED_COLOR_ID_GREEN; |
1346 | u32 reg; |
1347 | int ret; |
1348 | |
1349 | ret = of_property_read_u32(np, propname: "reg", out_value: ®); |
1350 | if (ret || !reg || reg > lpg->num_channels) |
1351 | return dev_err_probe(dev: lpg->dev, err: -EINVAL, fmt: "invalid \"reg\" of %pOFn\n", np); |
1352 | |
1353 | chan = &lpg->channels[reg - 1]; |
1354 | chan->in_use = true; |
1355 | |
1356 | ret = of_property_read_u32(np, propname: "color", out_value: &color); |
1357 | if (ret < 0 && ret != -EINVAL) |
1358 | return dev_err_probe(dev: lpg->dev, err: ret, |
1359 | fmt: "failed to parse \"color\" of %pOF\n", np); |
1360 | |
1361 | chan->color = color; |
1362 | |
1363 | *channel = chan; |
1364 | |
1365 | return 0; |
1366 | } |
1367 | |
1368 | static int lpg_add_led(struct lpg *lpg, struct device_node *np) |
1369 | { |
1370 | struct led_init_data init_data = {}; |
1371 | struct led_classdev *cdev; |
1372 | struct mc_subled *info; |
1373 | struct lpg_led *led; |
1374 | const char *state; |
1375 | int num_channels; |
1376 | u32 color = 0; |
1377 | int ret; |
1378 | int i; |
1379 | |
1380 | ret = of_property_read_u32(np, propname: "color", out_value: &color); |
1381 | if (ret < 0 && ret != -EINVAL) |
1382 | return dev_err_probe(dev: lpg->dev, err: ret, |
1383 | fmt: "failed to parse \"color\" of %pOF\n", np); |
1384 | |
1385 | if (color == LED_COLOR_ID_RGB) |
1386 | num_channels = of_get_available_child_count(np); |
1387 | else |
1388 | num_channels = 1; |
1389 | |
1390 | led = devm_kzalloc(dev: lpg->dev, struct_size(led, channels, num_channels), GFP_KERNEL); |
1391 | if (!led) |
1392 | return -ENOMEM; |
1393 | |
1394 | led->lpg = lpg; |
1395 | led->num_channels = num_channels; |
1396 | |
1397 | if (color == LED_COLOR_ID_RGB) { |
1398 | info = devm_kcalloc(dev: lpg->dev, n: num_channels, size: sizeof(*info), GFP_KERNEL); |
1399 | if (!info) |
1400 | return -ENOMEM; |
1401 | i = 0; |
1402 | for_each_available_child_of_node_scoped(np, child) { |
1403 | ret = lpg_parse_channel(lpg, np: child, channel: &led->channels[i]); |
1404 | if (ret < 0) |
1405 | return ret; |
1406 | |
1407 | info[i].color_index = led->channels[i]->color; |
1408 | info[i].intensity = 0; |
1409 | i++; |
1410 | } |
1411 | |
1412 | led->mcdev.subled_info = info; |
1413 | led->mcdev.num_colors = num_channels; |
1414 | |
1415 | cdev = &led->mcdev.led_cdev; |
1416 | cdev->brightness_set_blocking = lpg_brightness_mc_set; |
1417 | cdev->blink_set = lpg_blink_mc_set; |
1418 | |
1419 | /* Register pattern accessors if we have a LUT block or when using PPG */ |
1420 | if (lpg->lut_base || lpg->lpg_chan_sdam) { |
1421 | cdev->pattern_set = lpg_pattern_mc_set; |
1422 | cdev->pattern_clear = lpg_pattern_mc_clear; |
1423 | } |
1424 | } else { |
1425 | ret = lpg_parse_channel(lpg, np, channel: &led->channels[0]); |
1426 | if (ret < 0) |
1427 | return ret; |
1428 | |
1429 | cdev = &led->cdev; |
1430 | cdev->brightness_set_blocking = lpg_brightness_single_set; |
1431 | cdev->blink_set = lpg_blink_single_set; |
1432 | |
1433 | /* Register pattern accessors if we have a LUT block or when using PPG */ |
1434 | if (lpg->lut_base || lpg->lpg_chan_sdam) { |
1435 | cdev->pattern_set = lpg_pattern_single_set; |
1436 | cdev->pattern_clear = lpg_pattern_single_clear; |
1437 | } |
1438 | } |
1439 | |
1440 | cdev->default_trigger = of_get_property(node: np, name: "linux,default-trigger", NULL); |
1441 | |
1442 | if (lpg->lpg_chan_sdam) |
1443 | cdev->max_brightness = PPG_MAX_LED_BRIGHTNESS; |
1444 | else |
1445 | cdev->max_brightness = LPG_RESOLUTION_9BIT - 1; |
1446 | |
1447 | if (!of_property_read_string(np, propname: "default-state", out_string: &state) && |
1448 | !strcmp(state, "on")) |
1449 | cdev->brightness = cdev->max_brightness; |
1450 | else |
1451 | cdev->brightness = LED_OFF; |
1452 | |
1453 | cdev->brightness_set_blocking(cdev, cdev->brightness); |
1454 | |
1455 | init_data.fwnode = of_fwnode_handle(np); |
1456 | |
1457 | if (color == LED_COLOR_ID_RGB) |
1458 | ret = devm_led_classdev_multicolor_register_ext(parent: lpg->dev, mcled_cdev: &led->mcdev, init_data: &init_data); |
1459 | else |
1460 | ret = devm_led_classdev_register_ext(parent: lpg->dev, led_cdev: &led->cdev, init_data: &init_data); |
1461 | if (ret) |
1462 | dev_err_probe(dev: lpg->dev, err: ret, fmt: "unable to register %s\n", cdev->name); |
1463 | |
1464 | return ret; |
1465 | } |
1466 | |
1467 | static int lpg_init_channels(struct lpg *lpg) |
1468 | { |
1469 | const struct lpg_data *data = lpg->data; |
1470 | struct lpg_channel *chan; |
1471 | int i; |
1472 | |
1473 | lpg->num_channels = data->num_channels; |
1474 | lpg->channels = devm_kcalloc(dev: lpg->dev, n: data->num_channels, |
1475 | size: sizeof(struct lpg_channel), GFP_KERNEL); |
1476 | if (!lpg->channels) |
1477 | return -ENOMEM; |
1478 | |
1479 | for (i = 0; i < data->num_channels; i++) { |
1480 | chan = &lpg->channels[i]; |
1481 | |
1482 | chan->lpg = lpg; |
1483 | chan->base = data->channels[i].base; |
1484 | chan->triled_mask = data->channels[i].triled_mask; |
1485 | chan->lut_mask = BIT(i); |
1486 | chan->sdam_offset = data->channels[i].sdam_offset; |
1487 | |
1488 | regmap_read(map: lpg->map, reg: chan->base + LPG_SUBTYPE_REG, val: &chan->subtype); |
1489 | } |
1490 | |
1491 | return 0; |
1492 | } |
1493 | |
1494 | static int lpg_init_triled(struct lpg *lpg) |
1495 | { |
1496 | struct device_node *np = lpg->dev->of_node; |
1497 | int ret; |
1498 | |
1499 | /* Skip initialization if we don't have a triled block */ |
1500 | if (!lpg->data->triled_base) |
1501 | return 0; |
1502 | |
1503 | lpg->triled_base = lpg->data->triled_base; |
1504 | lpg->triled_has_atc_ctl = lpg->data->triled_has_atc_ctl; |
1505 | lpg->triled_has_src_sel = lpg->data->triled_has_src_sel; |
1506 | |
1507 | if (lpg->triled_has_src_sel) { |
1508 | ret = of_property_read_u32(np, propname: "qcom,power-source", out_value: &lpg->triled_src); |
1509 | if (ret || lpg->triled_src == 2 || lpg->triled_src > 3) |
1510 | return dev_err_probe(dev: lpg->dev, err: -EINVAL, |
1511 | fmt: "invalid power source\n"); |
1512 | } |
1513 | |
1514 | /* Disable automatic trickle charge LED */ |
1515 | if (lpg->triled_has_atc_ctl) |
1516 | regmap_write(map: lpg->map, reg: lpg->triled_base + TRI_LED_ATC_CTL, val: 0); |
1517 | |
1518 | /* Configure power source */ |
1519 | if (lpg->triled_has_src_sel) |
1520 | regmap_write(map: lpg->map, reg: lpg->triled_base + TRI_LED_SRC_SEL, val: lpg->triled_src); |
1521 | |
1522 | /* Default all outputs to off */ |
1523 | regmap_write(map: lpg->map, reg: lpg->triled_base + TRI_LED_EN_CTL, val: 0); |
1524 | |
1525 | return 0; |
1526 | } |
1527 | |
1528 | static int lpg_init_lut(struct lpg *lpg) |
1529 | { |
1530 | const struct lpg_data *data = lpg->data; |
1531 | |
1532 | if (!data->lut_size) |
1533 | return 0; |
1534 | |
1535 | lpg->lut_size = data->lut_size; |
1536 | if (data->lut_base) |
1537 | lpg->lut_base = data->lut_base; |
1538 | |
1539 | lpg->lut_bitmap = devm_bitmap_zalloc(dev: lpg->dev, nbits: lpg->lut_size, GFP_KERNEL); |
1540 | if (!lpg->lut_bitmap) |
1541 | return -ENOMEM; |
1542 | |
1543 | return 0; |
1544 | } |
1545 | |
1546 | static int lpg_init_sdam(struct lpg *lpg) |
1547 | { |
1548 | int i, sdam_count, rc; |
1549 | u8 val = 0; |
1550 | |
1551 | sdam_count = of_property_count_strings(np: lpg->dev->of_node, propname: "nvmem-names"); |
1552 | if (sdam_count <= 0) |
1553 | return 0; |
1554 | if (sdam_count > SDAM_MAX_DEVICES) |
1555 | return -EINVAL; |
1556 | |
1557 | /* Get the 1st SDAM device for LPG/LUT config */ |
1558 | lpg->lpg_chan_sdam = devm_nvmem_device_get(dev: lpg->dev, name: "lpg_chan_sdam"); |
1559 | if (IS_ERR(ptr: lpg->lpg_chan_sdam)) |
1560 | return dev_err_probe(dev: lpg->dev, err: PTR_ERR(ptr: lpg->lpg_chan_sdam), |
1561 | fmt: "Failed to get LPG chan SDAM device\n"); |
1562 | |
1563 | if (sdam_count == 1) { |
1564 | /* Get PBS device node if single SDAM device */ |
1565 | lpg->pbs_dev = get_pbs_client_device(client_dev: lpg->dev); |
1566 | if (IS_ERR(ptr: lpg->pbs_dev)) |
1567 | return dev_err_probe(dev: lpg->dev, err: PTR_ERR(ptr: lpg->pbs_dev), |
1568 | fmt: "Failed to get PBS client device\n"); |
1569 | } else if (sdam_count == 2) { |
1570 | /* Get the 2nd SDAM device for LUT pattern */ |
1571 | lpg->lut_sdam = devm_nvmem_device_get(dev: lpg->dev, name: "lut_sdam"); |
1572 | if (IS_ERR(ptr: lpg->lut_sdam)) |
1573 | return dev_err_probe(dev: lpg->dev, err: PTR_ERR(ptr: lpg->lut_sdam), |
1574 | fmt: "Failed to get LPG LUT SDAM device\n"); |
1575 | } |
1576 | |
1577 | for (i = 0; i < lpg->num_channels; i++) { |
1578 | struct lpg_channel *chan = &lpg->channels[i]; |
1579 | |
1580 | if (chan->sdam_offset) { |
1581 | rc = nvmem_device_write(nvmem: lpg->lpg_chan_sdam, |
1582 | SDAM_PBS_SCRATCH_LUT_COUNTER_OFFSET + chan->sdam_offset, bytes: 1, buf: &val); |
1583 | if (rc < 0) |
1584 | return rc; |
1585 | |
1586 | rc = lpg_sdam_configure_triggers(chan, set_trig: 0); |
1587 | if (rc < 0) |
1588 | return rc; |
1589 | |
1590 | rc = lpg_clear_pbs_trigger(lpg: chan->lpg, lut_mask: chan->lut_mask); |
1591 | if (rc < 0) |
1592 | return rc; |
1593 | } |
1594 | } |
1595 | |
1596 | return 0; |
1597 | } |
1598 | |
1599 | static int lpg_probe(struct platform_device *pdev) |
1600 | { |
1601 | struct lpg *lpg; |
1602 | int ret; |
1603 | int i; |
1604 | |
1605 | lpg = devm_kzalloc(dev: &pdev->dev, size: sizeof(*lpg), GFP_KERNEL); |
1606 | if (!lpg) |
1607 | return -ENOMEM; |
1608 | |
1609 | lpg->data = of_device_get_match_data(dev: &pdev->dev); |
1610 | if (!lpg->data) |
1611 | return -EINVAL; |
1612 | |
1613 | lpg->dev = &pdev->dev; |
1614 | mutex_init(&lpg->lock); |
1615 | |
1616 | lpg->map = dev_get_regmap(dev: pdev->dev.parent, NULL); |
1617 | if (!lpg->map) |
1618 | return dev_err_probe(dev: &pdev->dev, err: -ENXIO, fmt: "parent regmap unavailable\n"); |
1619 | |
1620 | ret = lpg_init_channels(lpg); |
1621 | if (ret < 0) |
1622 | return ret; |
1623 | |
1624 | ret = lpg_parse_dtest(lpg); |
1625 | if (ret < 0) |
1626 | return ret; |
1627 | |
1628 | ret = lpg_init_triled(lpg); |
1629 | if (ret < 0) |
1630 | return ret; |
1631 | |
1632 | ret = lpg_init_sdam(lpg); |
1633 | if (ret < 0) |
1634 | return ret; |
1635 | |
1636 | ret = lpg_init_lut(lpg); |
1637 | if (ret < 0) |
1638 | return ret; |
1639 | |
1640 | for_each_available_child_of_node_scoped(pdev->dev.of_node, np) { |
1641 | ret = lpg_add_led(lpg, np); |
1642 | if (ret) |
1643 | return ret; |
1644 | } |
1645 | |
1646 | for (i = 0; i < lpg->num_channels; i++) |
1647 | lpg_apply_dtest(chan: &lpg->channels[i]); |
1648 | |
1649 | return lpg_add_pwm(lpg); |
1650 | } |
1651 | |
1652 | static const struct lpg_data pm660l_lpg_data = { |
1653 | .lut_base = 0xb000, |
1654 | .lut_size = 49, |
1655 | |
1656 | .triled_base = 0xd000, |
1657 | .triled_has_atc_ctl = true, |
1658 | .triled_has_src_sel = true, |
1659 | |
1660 | .num_channels = 4, |
1661 | .channels = (const struct lpg_channel_data[]) { |
1662 | { .base = 0xb100, .triled_mask = BIT(5) }, |
1663 | { .base = 0xb200, .triled_mask = BIT(6) }, |
1664 | { .base = 0xb300, .triled_mask = BIT(7) }, |
1665 | { .base = 0xb400 }, |
1666 | }, |
1667 | }; |
1668 | |
1669 | static const struct lpg_data pm8916_pwm_data = { |
1670 | .num_channels = 1, |
1671 | .channels = (const struct lpg_channel_data[]) { |
1672 | { .base = 0xbc00 }, |
1673 | }, |
1674 | }; |
1675 | |
1676 | static const struct lpg_data pm8941_lpg_data = { |
1677 | .lut_base = 0xb000, |
1678 | .lut_size = 64, |
1679 | |
1680 | .triled_base = 0xd000, |
1681 | .triled_has_atc_ctl = true, |
1682 | .triled_has_src_sel = true, |
1683 | |
1684 | .num_channels = 8, |
1685 | .channels = (const struct lpg_channel_data[]) { |
1686 | { .base = 0xb100 }, |
1687 | { .base = 0xb200 }, |
1688 | { .base = 0xb300 }, |
1689 | { .base = 0xb400 }, |
1690 | { .base = 0xb500, .triled_mask = BIT(5) }, |
1691 | { .base = 0xb600, .triled_mask = BIT(6) }, |
1692 | { .base = 0xb700, .triled_mask = BIT(7) }, |
1693 | { .base = 0xb800 }, |
1694 | }, |
1695 | }; |
1696 | |
1697 | static const struct lpg_data pmi8950_pwm_data = { |
1698 | .num_channels = 1, |
1699 | .channels = (const struct lpg_channel_data[]) { |
1700 | { .base = 0xb000 }, |
1701 | }, |
1702 | }; |
1703 | |
1704 | static const struct lpg_data pm8994_lpg_data = { |
1705 | .lut_base = 0xb000, |
1706 | .lut_size = 64, |
1707 | |
1708 | .num_channels = 6, |
1709 | .channels = (const struct lpg_channel_data[]) { |
1710 | { .base = 0xb100 }, |
1711 | { .base = 0xb200 }, |
1712 | { .base = 0xb300 }, |
1713 | { .base = 0xb400 }, |
1714 | { .base = 0xb500 }, |
1715 | { .base = 0xb600 }, |
1716 | }, |
1717 | }; |
1718 | |
1719 | /* PMI632 uses SDAM instead of LUT for pattern */ |
1720 | static const struct lpg_data pmi632_lpg_data = { |
1721 | .triled_base = 0xd000, |
1722 | |
1723 | .lut_size = 64, |
1724 | |
1725 | .num_channels = 5, |
1726 | .channels = (const struct lpg_channel_data[]) { |
1727 | { .base = 0xb300, .triled_mask = BIT(7), .sdam_offset = 0x48 }, |
1728 | { .base = 0xb400, .triled_mask = BIT(6), .sdam_offset = 0x56 }, |
1729 | { .base = 0xb500, .triled_mask = BIT(5), .sdam_offset = 0x64 }, |
1730 | { .base = 0xb600 }, |
1731 | { .base = 0xb700 }, |
1732 | }, |
1733 | }; |
1734 | |
1735 | static const struct lpg_data pmi8994_lpg_data = { |
1736 | .lut_base = 0xb000, |
1737 | .lut_size = 24, |
1738 | |
1739 | .triled_base = 0xd000, |
1740 | .triled_has_atc_ctl = true, |
1741 | .triled_has_src_sel = true, |
1742 | |
1743 | .num_channels = 4, |
1744 | .channels = (const struct lpg_channel_data[]) { |
1745 | { .base = 0xb100, .triled_mask = BIT(5) }, |
1746 | { .base = 0xb200, .triled_mask = BIT(6) }, |
1747 | { .base = 0xb300, .triled_mask = BIT(7) }, |
1748 | { .base = 0xb400 }, |
1749 | }, |
1750 | }; |
1751 | |
1752 | static const struct lpg_data pmi8998_lpg_data = { |
1753 | .lut_base = 0xb000, |
1754 | .lut_size = 49, |
1755 | |
1756 | .triled_base = 0xd000, |
1757 | |
1758 | .num_channels = 6, |
1759 | .channels = (const struct lpg_channel_data[]) { |
1760 | { .base = 0xb100 }, |
1761 | { .base = 0xb200 }, |
1762 | { .base = 0xb300, .triled_mask = BIT(5) }, |
1763 | { .base = 0xb400, .triled_mask = BIT(6) }, |
1764 | { .base = 0xb500, .triled_mask = BIT(7) }, |
1765 | { .base = 0xb600 }, |
1766 | }, |
1767 | }; |
1768 | |
1769 | static const struct lpg_data pm8150b_lpg_data = { |
1770 | .lut_base = 0xb000, |
1771 | .lut_size = 24, |
1772 | |
1773 | .triled_base = 0xd000, |
1774 | |
1775 | .num_channels = 2, |
1776 | .channels = (const struct lpg_channel_data[]) { |
1777 | { .base = 0xb100, .triled_mask = BIT(7) }, |
1778 | { .base = 0xb200, .triled_mask = BIT(6) }, |
1779 | }, |
1780 | }; |
1781 | |
1782 | static const struct lpg_data pm8150l_lpg_data = { |
1783 | .lut_base = 0xb000, |
1784 | .lut_size = 48, |
1785 | |
1786 | .triled_base = 0xd000, |
1787 | |
1788 | .num_channels = 5, |
1789 | .channels = (const struct lpg_channel_data[]) { |
1790 | { .base = 0xb100, .triled_mask = BIT(7) }, |
1791 | { .base = 0xb200, .triled_mask = BIT(6) }, |
1792 | { .base = 0xb300, .triled_mask = BIT(5) }, |
1793 | { .base = 0xbc00 }, |
1794 | { .base = 0xbd00 }, |
1795 | |
1796 | }, |
1797 | }; |
1798 | |
1799 | static const struct lpg_data pm8350c_pwm_data = { |
1800 | .triled_base = 0xef00, |
1801 | |
1802 | .lut_size = 122, |
1803 | |
1804 | .num_channels = 4, |
1805 | .channels = (const struct lpg_channel_data[]) { |
1806 | { .base = 0xe800, .triled_mask = BIT(7), .sdam_offset = 0x48 }, |
1807 | { .base = 0xe900, .triled_mask = BIT(6), .sdam_offset = 0x56 }, |
1808 | { .base = 0xea00, .triled_mask = BIT(5), .sdam_offset = 0x64 }, |
1809 | { .base = 0xeb00 }, |
1810 | }, |
1811 | }; |
1812 | |
1813 | static const struct lpg_data pmk8550_pwm_data = { |
1814 | .num_channels = 2, |
1815 | .channels = (const struct lpg_channel_data[]) { |
1816 | { .base = 0xe800 }, |
1817 | { .base = 0xe900 }, |
1818 | }, |
1819 | }; |
1820 | |
1821 | static const struct of_device_id lpg_of_table[] = { |
1822 | { .compatible = "qcom,pm660l-lpg", .data = &pm660l_lpg_data }, |
1823 | { .compatible = "qcom,pm8150b-lpg", .data = &pm8150b_lpg_data }, |
1824 | { .compatible = "qcom,pm8150l-lpg", .data = &pm8150l_lpg_data }, |
1825 | { .compatible = "qcom,pm8350c-pwm", .data = &pm8350c_pwm_data }, |
1826 | { .compatible = "qcom,pm8916-pwm", .data = &pm8916_pwm_data }, |
1827 | { .compatible = "qcom,pm8941-lpg", .data = &pm8941_lpg_data }, |
1828 | { .compatible = "qcom,pm8994-lpg", .data = &pm8994_lpg_data }, |
1829 | { .compatible = "qcom,pmi632-lpg", .data = &pmi632_lpg_data }, |
1830 | { .compatible = "qcom,pmi8950-pwm", .data = &pmi8950_pwm_data }, |
1831 | { .compatible = "qcom,pmi8994-lpg", .data = &pmi8994_lpg_data }, |
1832 | { .compatible = "qcom,pmi8998-lpg", .data = &pmi8998_lpg_data }, |
1833 | { .compatible = "qcom,pmc8180c-lpg", .data = &pm8150l_lpg_data }, |
1834 | { .compatible = "qcom,pmk8550-pwm", .data = &pmk8550_pwm_data }, |
1835 | {} |
1836 | }; |
1837 | MODULE_DEVICE_TABLE(of, lpg_of_table); |
1838 | |
1839 | static struct platform_driver lpg_driver = { |
1840 | .probe = lpg_probe, |
1841 | .driver = { |
1842 | .name = "qcom-spmi-lpg", |
1843 | .of_match_table = lpg_of_table, |
1844 | }, |
1845 | }; |
1846 | module_platform_driver(lpg_driver); |
1847 | |
1848 | MODULE_DESCRIPTION("Qualcomm LPG LED driver"); |
1849 | MODULE_LICENSE("GPL v2"); |
1850 |
Definitions
- lpg
- lpg_channel
- lpg_led
- lpg_channel_data
- lpg_data
- lpg_clear_pbs_trigger
- lpg_set_pbs_trigger
- lpg_sdam_configure_triggers
- triled_set
- lpg_lut_store_sdam
- lpg_lut_store
- lpg_lut_free
- lpg_lut_sync
- lpg_clk_rates
- lpg_clk_rates_hi_res
- lpg_pre_divs
- lpg_pwm_resolution
- lpg_pwm_resolution_hi_res
- lpg_calc_freq
- lpg_calc_duty
- lpg_apply_freq
- lpg_enable_glitch
- lpg_disable_glitch
- lpg_apply_pwm_value
- lpg_sdam_apply_lut_control
- lpg_apply_lut_control
- lpg_apply_control
- lpg_apply_sync
- lpg_parse_dtest
- lpg_apply_dtest
- lpg_apply
- lpg_brightness_set
- lpg_brightness_single_set
- lpg_brightness_mc_set
- lpg_blink_set
- lpg_blink_single_set
- lpg_blink_mc_set
- lpg_pattern_set
- lpg_pattern_single_set
- lpg_pattern_mc_set
- lpg_pattern_clear
- lpg_pattern_single_clear
- lpg_pattern_mc_clear
- lpg_pwm_from_chip
- lpg_pwm_request
- lpg_pwm_apply
- lpg_pwm_get_state
- lpg_pwm_ops
- lpg_add_pwm
- lpg_parse_channel
- lpg_add_led
- lpg_init_channels
- lpg_init_triled
- lpg_init_lut
- lpg_init_sdam
- lpg_probe
- pm660l_lpg_data
- pm8916_pwm_data
- pm8941_lpg_data
- pmi8950_pwm_data
- pm8994_lpg_data
- pmi632_lpg_data
- pmi8994_lpg_data
- pmi8998_lpg_data
- pm8150b_lpg_data
- pm8150l_lpg_data
- pm8350c_pwm_data
- pmk8550_pwm_data
- lpg_of_table
Improve your Profiling and Debugging skills
Find out more