Lines Matching +full:led +full:- +full:pattern

1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2017-2022 Linaro Ltd
4 * Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
8 #include <linux/led-class-multicolor.h>
37 #define PWM_DTEST_REG(x) (0xe2 + (x) - 1)
53 * struct lpg - LPG device context
56 * @lock: used to synchronize LED and pwm callback requests
57 * @pwm: PWM-chip object, if operating in PWM mode
63 * @triled_src: power-source for the TRILED
93 * struct lpg_channel - per channel data
97 * @lut_mask: mask in LUT to start pattern generator for this channel
99 * @in_use: channel is exposed to LED framework
100 * @color: color of the LED attached to this channel
110 * @ramp_ping_pong: reverse through pattern, rather than wrapping to start
111 * @ramp_oneshot: perform only a single pass over the pattern
112 * @ramp_reverse: iterate over pattern backwards
113 * @ramp_tick_ms: length (in milliseconds) of one step in the pattern
114 * @ramp_lo_pause_ms: pause (in milliseconds) before iterating over pattern
115 * @ramp_hi_pause_ms: pause (in milliseconds) after iterating over pattern
116 * @pattern_lo_idx: start index of associated pattern
117 * @pattern_hi_idx: last index of associated pattern
155 * struct lpg_led - logical LED object
157 * @cdev: LED class device
158 * @mcdev: Multicolor LED class device
160 * @channels: list of channels associated with the LED
173 * struct lpg_channel_data - per channel initialization data
183 * struct lpg_data - initialization data
205 if (!lpg->triled_base) in triled_set()
208 return regmap_update_bits(lpg->map, lpg->triled_base + TRI_LED_EN_CTL, in triled_set()
212 static int lpg_lut_store(struct lpg *lpg, struct led_pattern *pattern, in lpg_lut_store() argument
219 idx = bitmap_find_next_zero_area(lpg->lut_bitmap, lpg->lut_size, in lpg_lut_store()
221 if (idx >= lpg->lut_size) in lpg_lut_store()
222 return -ENOMEM; in lpg_lut_store()
225 val = pattern[i].brightness; in lpg_lut_store()
227 regmap_bulk_write(lpg->map, lpg->lut_base + LPG_LUT_REG(idx + i), in lpg_lut_store()
231 bitmap_set(lpg->lut_bitmap, idx, len); in lpg_lut_store()
234 *hi_idx = idx + len - 1; in lpg_lut_store()
243 len = hi_idx - lo_idx + 1; in lpg_lut_free()
247 bitmap_clear(lpg->lut_bitmap, lo_idx, len); in lpg_lut_free()
252 return regmap_write(lpg->map, lpg->lut_base + RAMP_CONTROL_REG, mask); in lpg_lut_sync()
272 * period = -------------------------- in lpg_calc_freq()
283 return -EINVAL; in lpg_calc_freq()
295 * M = log2 ------------------------------------- in lpg_calc_freq()
316 error = period - actual; in lpg_calc_freq()
328 chan->clk_sel = best_clk; in lpg_calc_freq()
329 chan->pre_div_sel = best_div; in lpg_calc_freq()
330 chan->pre_div_exp = best_m; in lpg_calc_freq()
331 chan->period = best_period; in lpg_calc_freq()
338 unsigned int max = LPG_RESOLUTION - 1; in lpg_calc_duty()
341 val = div64_u64(duty * lpg_clk_rates[chan->clk_sel], in lpg_calc_duty()
342 (u64)NSEC_PER_SEC * lpg_pre_divs[chan->pre_div_sel] * (1 << chan->pre_div_exp)); in lpg_calc_duty()
344 chan->pwm_value = min(val, max); in lpg_calc_duty()
350 struct lpg *lpg = chan->lpg; in lpg_apply_freq()
352 if (!chan->enabled) in lpg_apply_freq()
355 val = chan->clk_sel; in lpg_apply_freq()
358 switch (chan->subtype) { in lpg_apply_freq()
371 regmap_write(lpg->map, chan->base + LPG_SIZE_CLK_REG, val); in lpg_apply_freq()
373 val = FIELD_PREP(PWM_FREQ_PRE_DIV_MASK, chan->pre_div_sel) | in lpg_apply_freq()
374 FIELD_PREP(PWM_FREQ_EXP_MASK, chan->pre_div_exp); in lpg_apply_freq()
375 regmap_write(lpg->map, chan->base + LPG_PREDIV_CLK_REG, val); in lpg_apply_freq()
382 struct lpg *lpg = chan->lpg; in lpg_enable_glitch()
384 regmap_update_bits(lpg->map, chan->base + PWM_TYPE_CONFIG_REG, in lpg_enable_glitch()
390 struct lpg *lpg = chan->lpg; in lpg_disable_glitch()
392 regmap_update_bits(lpg->map, chan->base + PWM_TYPE_CONFIG_REG, in lpg_disable_glitch()
399 struct lpg *lpg = chan->lpg; in lpg_apply_pwm_value()
400 u16 val = chan->pwm_value; in lpg_apply_pwm_value()
402 if (!chan->enabled) in lpg_apply_pwm_value()
405 regmap_bulk_write(lpg->map, chan->base + PWM_VALUE_REG, &val, sizeof(val)); in lpg_apply_pwm_value()
416 struct lpg *lpg = chan->lpg; in lpg_apply_lut_control()
420 unsigned int lo_idx = chan->pattern_lo_idx; in lpg_apply_lut_control()
421 unsigned int hi_idx = chan->pattern_hi_idx; in lpg_apply_lut_control()
422 u16 step = chan->ramp_tick_ms; in lpg_apply_lut_control()
424 if (!chan->ramp_enabled || chan->pattern_lo_idx == chan->pattern_hi_idx) in lpg_apply_lut_control()
427 hi_pause = DIV_ROUND_UP(chan->ramp_hi_pause_ms, step); in lpg_apply_lut_control()
428 lo_pause = DIV_ROUND_UP(chan->ramp_lo_pause_ms, step); in lpg_apply_lut_control()
430 if (!chan->ramp_reverse) in lpg_apply_lut_control()
432 if (!chan->ramp_oneshot) in lpg_apply_lut_control()
434 if (chan->ramp_ping_pong) in lpg_apply_lut_control()
436 if (chan->ramp_hi_pause_ms) in lpg_apply_lut_control()
438 if (chan->ramp_lo_pause_ms) in lpg_apply_lut_control()
441 regmap_write(lpg->map, chan->base + LPG_PATTERN_CONFIG_REG, conf); in lpg_apply_lut_control()
442 regmap_write(lpg->map, chan->base + LPG_HI_IDX_REG, hi_idx); in lpg_apply_lut_control()
443 regmap_write(lpg->map, chan->base + LPG_LO_IDX_REG, lo_idx); in lpg_apply_lut_control()
445 regmap_bulk_write(lpg->map, chan->base + LPG_RAMP_DURATION_REG, &step, sizeof(step)); in lpg_apply_lut_control()
446 regmap_write(lpg->map, chan->base + LPG_HI_PAUSE_REG, hi_pause); in lpg_apply_lut_control()
447 regmap_write(lpg->map, chan->base + LPG_LO_PAUSE_REG, lo_pause); in lpg_apply_lut_control()
458 struct lpg *lpg = chan->lpg; in lpg_apply_control()
462 if (chan->enabled) in lpg_apply_control()
465 if (chan->pattern_lo_idx != chan->pattern_hi_idx) in lpg_apply_control()
470 regmap_write(lpg->map, chan->base + PWM_ENABLE_CONTROL_REG, ctrl); in lpg_apply_control()
476 if (chan->enabled) in lpg_apply_control()
484 struct lpg *lpg = chan->lpg; in lpg_apply_sync()
486 regmap_write(lpg->map, chan->base + PWM_SYNC_REG, LPG_SYNC_PWM); in lpg_apply_sync()
492 struct device_node *np = lpg->dev->of_node; in lpg_parse_dtest()
498 if (count == -EINVAL) { in lpg_parse_dtest()
503 } else if (count != lpg->data->num_channels * 2) { in lpg_parse_dtest()
504 dev_err(lpg->dev, "qcom,dtest needs to be %d items\n", in lpg_parse_dtest()
505 lpg->data->num_channels * 2); in lpg_parse_dtest()
506 return -EINVAL; in lpg_parse_dtest()
509 for (i = 0; i < lpg->data->num_channels; i++) { in lpg_parse_dtest()
510 chan = &lpg->channels[i]; in lpg_parse_dtest()
513 &chan->dtest_line); in lpg_parse_dtest()
518 &chan->dtest_value); in lpg_parse_dtest()
526 dev_err(lpg->dev, "malformed qcom,dtest\n"); in lpg_parse_dtest()
532 struct lpg *lpg = chan->lpg; in lpg_apply_dtest()
534 if (!chan->dtest_line) in lpg_apply_dtest()
537 regmap_write(lpg->map, chan->base + PWM_SEC_ACCESS_REG, 0xa5); in lpg_apply_dtest()
538 regmap_write(lpg->map, chan->base + PWM_DTEST_REG(chan->dtest_line), in lpg_apply_dtest()
539 chan->dtest_value); in lpg_apply_dtest()
553 static void lpg_brightness_set(struct lpg_led *led, struct led_classdev *cdev, in lpg_brightness_set() argument
562 struct lpg *lpg = led->lpg; in lpg_brightness_set()
565 for (i = 0; i < led->num_channels; i++) { in lpg_brightness_set()
566 chan = led->channels[i]; in lpg_brightness_set()
570 chan->enabled = false; in lpg_brightness_set()
571 chan->ramp_enabled = false; in lpg_brightness_set()
572 } else if (chan->pattern_lo_idx != chan->pattern_hi_idx) { in lpg_brightness_set()
575 chan->enabled = true; in lpg_brightness_set()
576 chan->ramp_enabled = true; in lpg_brightness_set()
578 lut_mask |= chan->lut_mask; in lpg_brightness_set()
579 triled_enabled |= chan->triled_mask; in lpg_brightness_set()
583 duty = div_u64(brightness * chan->period, cdev->max_brightness); in lpg_brightness_set()
585 chan->enabled = true; in lpg_brightness_set()
586 chan->ramp_enabled = false; in lpg_brightness_set()
588 triled_enabled |= chan->triled_mask; in lpg_brightness_set()
591 triled_mask |= chan->triled_mask; in lpg_brightness_set()
608 struct lpg_led *led = container_of(cdev, struct lpg_led, cdev); in lpg_brightness_single_set() local
611 mutex_lock(&led->lpg->lock); in lpg_brightness_single_set()
614 lpg_brightness_set(led, cdev, &info); in lpg_brightness_single_set()
616 mutex_unlock(&led->lpg->lock); in lpg_brightness_single_set()
623 struct lpg_led *led = container_of(mc, struct lpg_led, mcdev); in lpg_brightness_mc_set() local
625 mutex_lock(&led->lpg->lock); in lpg_brightness_mc_set()
628 lpg_brightness_set(led, cdev, mc->subled_info); in lpg_brightness_mc_set()
630 mutex_unlock(&led->lpg->lock); in lpg_brightness_mc_set()
633 static int lpg_blink_set(struct lpg_led *led, in lpg_blink_set() argument
639 struct lpg *lpg = led->lpg; in lpg_blink_set()
651 for (i = 0; i < led->num_channels; i++) { in lpg_blink_set()
652 chan = led->channels[i]; in lpg_blink_set()
657 chan->enabled = true; in lpg_blink_set()
658 chan->ramp_enabled = false; in lpg_blink_set()
660 triled_mask |= chan->triled_mask; in lpg_blink_set()
668 chan = led->channels[0]; in lpg_blink_set()
669 duty = div_u64(chan->pwm_value * chan->period, LPG_RESOLUTION); in lpg_blink_set()
671 *delay_off = div_u64(chan->period - duty, NSEC_PER_MSEC); in lpg_blink_set()
679 struct lpg_led *led = container_of(cdev, struct lpg_led, cdev); in lpg_blink_single_set() local
682 mutex_lock(&led->lpg->lock); in lpg_blink_single_set()
684 ret = lpg_blink_set(led, delay_on, delay_off); in lpg_blink_single_set()
686 mutex_unlock(&led->lpg->lock); in lpg_blink_single_set()
695 struct lpg_led *led = container_of(mc, struct lpg_led, mcdev); in lpg_blink_mc_set() local
698 mutex_lock(&led->lpg->lock); in lpg_blink_mc_set()
700 ret = lpg_blink_set(led, delay_on, delay_off); in lpg_blink_mc_set()
702 mutex_unlock(&led->lpg->lock); in lpg_blink_mc_set()
707 static int lpg_pattern_set(struct lpg_led *led, struct led_pattern *led_pattern, in lpg_pattern_set() argument
711 struct lpg *lpg = led->lpg; in lpg_pattern_set()
712 struct led_pattern *pattern; in lpg_pattern_set() local
723 int ret = -EINVAL; in lpg_pattern_set()
726 if (repeat != -1 && repeat != 1) in lpg_pattern_set()
727 return -EINVAL; in lpg_pattern_set()
730 * The standardized leds-trigger-pattern format defines that the in lpg_pattern_set()
731 * brightness of the LED follows a linear transition from one entry in lpg_pattern_set()
732 * in the pattern to the next, over the given delta_t time. It in lpg_pattern_set()
733 * describes that the way to perform instant transitions a zero-length in lpg_pattern_set()
734 * entry should be added following a pattern entry. in lpg_pattern_set()
737 * transitions), so require each entry in the pattern to be followed by in lpg_pattern_set()
738 * a zero-length transition. in lpg_pattern_set()
741 return -EINVAL; in lpg_pattern_set()
743 pattern = kcalloc(len / 2, sizeof(*pattern), GFP_KERNEL); in lpg_pattern_set()
744 if (!pattern) in lpg_pattern_set()
745 return -ENOMEM; in lpg_pattern_set()
753 pattern[i / 2].brightness = led_pattern[i].brightness; in lpg_pattern_set()
754 pattern[i / 2].delta_t = led_pattern[i].delta_t; in lpg_pattern_set()
760 * Specifying a pattern of length 1 causes the hardware to iterate in lpg_pattern_set()
768 * used to stretch the first delay of the pattern and a "high pause" in lpg_pattern_set()
771 * In order to save space the pattern can be played in "ping pong" in lpg_pattern_set()
772 * mode, in which the pattern is first played forward, then "high in lpg_pattern_set()
773 * pause" is applied, then the pattern is played backwards and finally in lpg_pattern_set()
776 * The middle elements of the pattern are used to determine delta_t and in lpg_pattern_set()
779 * The first element in the pattern is used to determine "low pause". in lpg_pattern_set()
781 * If the specified pattern is a palindrome the ping pong mode is in lpg_pattern_set()
783 * last in the programmed pattern) determines the "high pause". in lpg_pattern_set()
788 brightness_a = pattern[i].brightness; in lpg_pattern_set()
789 brightness_b = pattern[len - i - 1].brightness; in lpg_pattern_set()
797 /* The pattern length to be written to the LUT */ in lpg_pattern_set()
804 * Validate that all delta_t in the pattern are the same, with the in lpg_pattern_set()
807 delta_t = pattern[1].delta_t; in lpg_pattern_set()
809 if (pattern[i].delta_t != delta_t) { in lpg_pattern_set()
811 * Allow last entry in the full or shortened pattern to in lpg_pattern_set()
814 if (i != actual_len - 1) in lpg_pattern_set()
823 /* Find "low pause" and "high pause" in the pattern */ in lpg_pattern_set()
824 lo_pause = pattern[0].delta_t; in lpg_pattern_set()
825 hi_pause = pattern[actual_len - 1].delta_t; in lpg_pattern_set()
827 mutex_lock(&lpg->lock); in lpg_pattern_set()
828 ret = lpg_lut_store(lpg, pattern, actual_len, &lo_idx, &hi_idx); in lpg_pattern_set()
832 for (i = 0; i < led->num_channels; i++) { in lpg_pattern_set()
833 chan = led->channels[i]; in lpg_pattern_set()
835 chan->ramp_tick_ms = delta_t; in lpg_pattern_set()
836 chan->ramp_ping_pong = ping_pong; in lpg_pattern_set()
837 chan->ramp_oneshot = repeat != -1; in lpg_pattern_set()
839 chan->ramp_lo_pause_ms = lo_pause; in lpg_pattern_set()
840 chan->ramp_hi_pause_ms = hi_pause; in lpg_pattern_set()
842 chan->pattern_lo_idx = lo_idx; in lpg_pattern_set()
843 chan->pattern_hi_idx = hi_idx; in lpg_pattern_set()
847 mutex_unlock(&lpg->lock); in lpg_pattern_set()
849 kfree(pattern); in lpg_pattern_set()
855 struct led_pattern *pattern, u32 len, in lpg_pattern_single_set() argument
858 struct lpg_led *led = container_of(cdev, struct lpg_led, cdev); in lpg_pattern_single_set() local
861 ret = lpg_pattern_set(led, pattern, len, repeat); in lpg_pattern_single_set()
871 struct led_pattern *pattern, u32 len, in lpg_pattern_mc_set() argument
875 struct lpg_led *led = container_of(mc, struct lpg_led, mcdev); in lpg_pattern_mc_set() local
878 ret = lpg_pattern_set(led, pattern, len, repeat); in lpg_pattern_mc_set()
883 lpg_brightness_set(led, cdev, mc->subled_info); in lpg_pattern_mc_set()
888 static int lpg_pattern_clear(struct lpg_led *led) in lpg_pattern_clear() argument
891 struct lpg *lpg = led->lpg; in lpg_pattern_clear()
894 mutex_lock(&lpg->lock); in lpg_pattern_clear()
896 chan = led->channels[0]; in lpg_pattern_clear()
897 lpg_lut_free(lpg, chan->pattern_lo_idx, chan->pattern_hi_idx); in lpg_pattern_clear()
899 for (i = 0; i < led->num_channels; i++) { in lpg_pattern_clear()
900 chan = led->channels[i]; in lpg_pattern_clear()
901 chan->pattern_lo_idx = 0; in lpg_pattern_clear()
902 chan->pattern_hi_idx = 0; in lpg_pattern_clear()
905 mutex_unlock(&lpg->lock); in lpg_pattern_clear()
912 struct lpg_led *led = container_of(cdev, struct lpg_led, cdev); in lpg_pattern_single_clear() local
914 return lpg_pattern_clear(led); in lpg_pattern_single_clear()
920 struct lpg_led *led = container_of(mc, struct lpg_led, mcdev); in lpg_pattern_mc_clear() local
922 return lpg_pattern_clear(led); in lpg_pattern_mc_clear()
928 struct lpg_channel *chan = &lpg->channels[pwm->hwpwm]; in lpg_pwm_request()
930 return chan->in_use ? -EBUSY : 0; in lpg_pwm_request()
935 * - Updating both duty and period is not done atomically, so the output signal
937 * - Changed parameters takes effect immediately.
938 * - A disabled channel outputs a logical 0.
944 struct lpg_channel *chan = &lpg->channels[pwm->hwpwm]; in lpg_pwm_apply()
947 if (state->polarity != PWM_POLARITY_NORMAL) in lpg_pwm_apply()
948 return -EINVAL; in lpg_pwm_apply()
950 mutex_lock(&lpg->lock); in lpg_pwm_apply()
952 if (state->enabled) { in lpg_pwm_apply()
953 ret = lpg_calc_freq(chan, state->period); in lpg_pwm_apply()
957 lpg_calc_duty(chan, state->duty_cycle); in lpg_pwm_apply()
959 chan->enabled = state->enabled; in lpg_pwm_apply()
963 triled_set(lpg, chan->triled_mask, chan->enabled ? chan->triled_mask : 0); in lpg_pwm_apply()
966 mutex_unlock(&lpg->lock); in lpg_pwm_apply()
975 struct lpg_channel *chan = &lpg->channels[pwm->hwpwm]; in lpg_pwm_get_state()
983 ret = regmap_read(lpg->map, chan->base + LPG_SIZE_CLK_REG, &val); in lpg_pwm_get_state()
989 ret = regmap_read(lpg->map, chan->base + LPG_PREDIV_CLK_REG, &val); in lpg_pwm_get_state()
996 ret = regmap_bulk_read(lpg->map, chan->base + PWM_VALUE_REG, &pwm_value, sizeof(pwm_value)); in lpg_pwm_get_state()
1000 state->period = DIV_ROUND_UP_ULL((u64)NSEC_PER_SEC * LPG_RESOLUTION * pre_div * (1 << m), refclk); in lpg_pwm_get_state()
1001 state->duty_cycle = DIV_ROUND_UP_ULL((u64)NSEC_PER_SEC * pwm_value * pre_div * (1 << m), refclk); in lpg_pwm_get_state()
1003 state->period = 0; in lpg_pwm_get_state()
1004 state->duty_cycle = 0; in lpg_pwm_get_state()
1007 ret = regmap_read(lpg->map, chan->base + PWM_ENABLE_CONTROL_REG, &val); in lpg_pwm_get_state()
1011 state->enabled = FIELD_GET(LPG_ENABLE_CONTROL_OUTPUT, val); in lpg_pwm_get_state()
1012 state->polarity = PWM_POLARITY_NORMAL; in lpg_pwm_get_state()
1014 if (state->duty_cycle > state->period) in lpg_pwm_get_state()
1015 state->duty_cycle = state->period; in lpg_pwm_get_state()
1029 lpg->pwm.base = -1; in lpg_add_pwm()
1030 lpg->pwm.dev = lpg->dev; in lpg_add_pwm()
1031 lpg->pwm.npwm = lpg->num_channels; in lpg_add_pwm()
1032 lpg->pwm.ops = &lpg_pwm_ops; in lpg_add_pwm()
1034 ret = pwmchip_add(&lpg->pwm); in lpg_add_pwm()
1036 dev_err(lpg->dev, "failed to add PWM chip: ret %d\n", ret); in lpg_add_pwm()
1050 if (ret || !reg || reg > lpg->num_channels) { in lpg_parse_channel()
1051 dev_err(lpg->dev, "invalid \"reg\" of %pOFn\n", np); in lpg_parse_channel()
1052 return -EINVAL; in lpg_parse_channel()
1055 chan = &lpg->channels[reg - 1]; in lpg_parse_channel()
1056 chan->in_use = true; in lpg_parse_channel()
1059 if (ret < 0 && ret != -EINVAL) { in lpg_parse_channel()
1060 dev_err(lpg->dev, "failed to parse \"color\" of %pOF\n", np); in lpg_parse_channel()
1064 chan->color = color; in lpg_parse_channel()
1077 struct lpg_led *led; in lpg_add_led() local
1085 if (ret < 0 && ret != -EINVAL) { in lpg_add_led()
1086 dev_err(lpg->dev, "failed to parse \"color\" of %pOF\n", np); in lpg_add_led()
1095 led = devm_kzalloc(lpg->dev, struct_size(led, channels, num_channels), GFP_KERNEL); in lpg_add_led()
1096 if (!led) in lpg_add_led()
1097 return -ENOMEM; in lpg_add_led()
1099 led->lpg = lpg; in lpg_add_led()
1100 led->num_channels = num_channels; in lpg_add_led()
1103 info = devm_kcalloc(lpg->dev, num_channels, sizeof(*info), GFP_KERNEL); in lpg_add_led()
1105 return -ENOMEM; in lpg_add_led()
1108 ret = lpg_parse_channel(lpg, child, &led->channels[i]); in lpg_add_led()
1112 info[i].color_index = led->channels[i]->color; in lpg_add_led()
1117 led->mcdev.subled_info = info; in lpg_add_led()
1118 led->mcdev.num_colors = num_channels; in lpg_add_led()
1120 cdev = &led->mcdev.led_cdev; in lpg_add_led()
1121 cdev->brightness_set = lpg_brightness_mc_set; in lpg_add_led()
1122 cdev->blink_set = lpg_blink_mc_set; in lpg_add_led()
1124 /* Register pattern accessors only if we have a LUT block */ in lpg_add_led()
1125 if (lpg->lut_base) { in lpg_add_led()
1126 cdev->pattern_set = lpg_pattern_mc_set; in lpg_add_led()
1127 cdev->pattern_clear = lpg_pattern_mc_clear; in lpg_add_led()
1130 ret = lpg_parse_channel(lpg, np, &led->channels[0]); in lpg_add_led()
1134 cdev = &led->cdev; in lpg_add_led()
1135 cdev->brightness_set = lpg_brightness_single_set; in lpg_add_led()
1136 cdev->blink_set = lpg_blink_single_set; in lpg_add_led()
1138 /* Register pattern accessors only if we have a LUT block */ in lpg_add_led()
1139 if (lpg->lut_base) { in lpg_add_led()
1140 cdev->pattern_set = lpg_pattern_single_set; in lpg_add_led()
1141 cdev->pattern_clear = lpg_pattern_single_clear; in lpg_add_led()
1145 cdev->default_trigger = of_get_property(np, "linux,default-trigger", NULL); in lpg_add_led()
1146 cdev->max_brightness = LPG_RESOLUTION - 1; in lpg_add_led()
1148 if (!of_property_read_string(np, "default-state", &state) && in lpg_add_led()
1150 cdev->brightness = cdev->max_brightness; in lpg_add_led()
1152 cdev->brightness = LED_OFF; in lpg_add_led()
1154 cdev->brightness_set(cdev, cdev->brightness); in lpg_add_led()
1159 ret = devm_led_classdev_multicolor_register_ext(lpg->dev, &led->mcdev, &init_data); in lpg_add_led()
1161 ret = devm_led_classdev_register_ext(lpg->dev, &led->cdev, &init_data); in lpg_add_led()
1163 dev_err(lpg->dev, "unable to register %s\n", cdev->name); in lpg_add_led()
1170 const struct lpg_data *data = lpg->data; in lpg_init_channels()
1174 lpg->num_channels = data->num_channels; in lpg_init_channels()
1175 lpg->channels = devm_kcalloc(lpg->dev, data->num_channels, in lpg_init_channels()
1177 if (!lpg->channels) in lpg_init_channels()
1178 return -ENOMEM; in lpg_init_channels()
1180 for (i = 0; i < data->num_channels; i++) { in lpg_init_channels()
1181 chan = &lpg->channels[i]; in lpg_init_channels()
1183 chan->lpg = lpg; in lpg_init_channels()
1184 chan->base = data->channels[i].base; in lpg_init_channels()
1185 chan->triled_mask = data->channels[i].triled_mask; in lpg_init_channels()
1186 chan->lut_mask = BIT(i); in lpg_init_channels()
1188 regmap_read(lpg->map, chan->base + LPG_SUBTYPE_REG, &chan->subtype); in lpg_init_channels()
1196 struct device_node *np = lpg->dev->of_node; in lpg_init_triled()
1200 if (!lpg->data->triled_base) in lpg_init_triled()
1203 lpg->triled_base = lpg->data->triled_base; in lpg_init_triled()
1204 lpg->triled_has_atc_ctl = lpg->data->triled_has_atc_ctl; in lpg_init_triled()
1205 lpg->triled_has_src_sel = lpg->data->triled_has_src_sel; in lpg_init_triled()
1207 if (lpg->triled_has_src_sel) { in lpg_init_triled()
1208 ret = of_property_read_u32(np, "qcom,power-source", &lpg->triled_src); in lpg_init_triled()
1209 if (ret || lpg->triled_src == 2 || lpg->triled_src > 3) { in lpg_init_triled()
1210 dev_err(lpg->dev, "invalid power source\n"); in lpg_init_triled()
1211 return -EINVAL; in lpg_init_triled()
1215 /* Disable automatic trickle charge LED */ in lpg_init_triled()
1216 if (lpg->triled_has_atc_ctl) in lpg_init_triled()
1217 regmap_write(lpg->map, lpg->triled_base + TRI_LED_ATC_CTL, 0); in lpg_init_triled()
1220 if (lpg->triled_has_src_sel) in lpg_init_triled()
1221 regmap_write(lpg->map, lpg->triled_base + TRI_LED_SRC_SEL, lpg->triled_src); in lpg_init_triled()
1224 regmap_write(lpg->map, lpg->triled_base + TRI_LED_EN_CTL, 0); in lpg_init_triled()
1231 const struct lpg_data *data = lpg->data; in lpg_init_lut()
1233 if (!data->lut_base) in lpg_init_lut()
1236 lpg->lut_base = data->lut_base; in lpg_init_lut()
1237 lpg->lut_size = data->lut_size; in lpg_init_lut()
1239 lpg->lut_bitmap = devm_bitmap_zalloc(lpg->dev, lpg->lut_size, GFP_KERNEL); in lpg_init_lut()
1240 if (!lpg->lut_bitmap) in lpg_init_lut()
1241 return -ENOMEM; in lpg_init_lut()
1253 lpg = devm_kzalloc(&pdev->dev, sizeof(*lpg), GFP_KERNEL); in lpg_probe()
1255 return -ENOMEM; in lpg_probe()
1257 lpg->data = of_device_get_match_data(&pdev->dev); in lpg_probe()
1258 if (!lpg->data) in lpg_probe()
1259 return -EINVAL; in lpg_probe()
1263 lpg->dev = &pdev->dev; in lpg_probe()
1264 mutex_init(&lpg->lock); in lpg_probe()
1266 lpg->map = dev_get_regmap(pdev->dev.parent, NULL); in lpg_probe()
1267 if (!lpg->map) in lpg_probe()
1268 return dev_err_probe(&pdev->dev, -ENXIO, "parent regmap unavailable\n"); in lpg_probe()
1286 for_each_available_child_of_node(pdev->dev.of_node, np) { in lpg_probe()
1292 for (i = 0; i < lpg->num_channels; i++) in lpg_probe()
1293 lpg_apply_dtest(&lpg->channels[i]); in lpg_probe()
1302 pwmchip_remove(&lpg->pwm); in lpg_remove()
1427 { .compatible = "qcom,pm8150b-lpg", .data = &pm8150b_lpg_data },
1428 { .compatible = "qcom,pm8150l-lpg", .data = &pm8150l_lpg_data },
1429 { .compatible = "qcom,pm8350c-pwm", .data = &pm8350c_pwm_data },
1430 { .compatible = "qcom,pm8916-pwm", .data = &pm8916_pwm_data },
1431 { .compatible = "qcom,pm8941-lpg", .data = &pm8941_lpg_data },
1432 { .compatible = "qcom,pm8994-lpg", .data = &pm8994_lpg_data },
1433 { .compatible = "qcom,pmi8994-lpg", .data = &pmi8994_lpg_data },
1434 { .compatible = "qcom,pmi8998-lpg", .data = &pmi8998_lpg_data },
1435 { .compatible = "qcom,pmc8180c-lpg", .data = &pm8150l_lpg_data },
1444 .name = "qcom-spmi-lpg",
1450 MODULE_DESCRIPTION("Qualcomm LPG LED driver");