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