Lines Matching +full:pmi632 +full:- +full:pbs

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.
5 * Copyright (c) 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved.
9 #include <linux/led-class-multicolor.h>
11 #include <linux/nvmem-consumer.h>
17 #include <linux/soc/qcom/qcom-pbs.h>
42 #define PWM_DTEST_REG(x) (0xe2 + (x) - 1)
86 * struct lpg - LPG device context
90 * @pwm: PWM-chip object, if operating in PWM mode
95 * @pbs_dev: PBS device
98 * @pbs_en_bitmap: bitmap for tracking PBS triggers
100 * @triled_src: power-source for the TRILED
135 * struct lpg_channel - per channel data
201 * struct lpg_led - logical LED object
219 * struct lpg_channel_data - per channel initialization data
231 * struct lpg_data - initialization data
257 if (!lpg->lpg_chan_sdam) in lpg_clear_pbs_trigger()
260 lpg->pbs_en_bitmap &= (~lut_mask); in lpg_clear_pbs_trigger()
261 if (!lpg->pbs_en_bitmap) { in lpg_clear_pbs_trigger()
262 rc = nvmem_device_write(lpg->lpg_chan_sdam, SDAM_REG_PBS_SEQ_EN, 1, &val); in lpg_clear_pbs_trigger()
266 if (lpg->lut_sdam) { in lpg_clear_pbs_trigger()
268 rc = nvmem_device_write(lpg->lpg_chan_sdam, SDAM_PBS_TRIG_CLR, 1, &val); in lpg_clear_pbs_trigger()
282 if (!lpg->lpg_chan_sdam) in lpg_set_pbs_trigger()
285 if (!lpg->pbs_en_bitmap) { in lpg_set_pbs_trigger()
286 rc = nvmem_device_write(lpg->lpg_chan_sdam, SDAM_REG_PBS_SEQ_EN, 1, &val); in lpg_set_pbs_trigger()
290 if (lpg->lut_sdam) { in lpg_set_pbs_trigger()
291 rc = nvmem_device_write(lpg->lpg_chan_sdam, SDAM_PBS_TRIG_SET, 1, &val); in lpg_set_pbs_trigger()
295 rc = qcom_pbs_trigger_event(lpg->pbs_dev, val); in lpg_set_pbs_trigger()
300 lpg->pbs_en_bitmap |= lut_mask; in lpg_set_pbs_trigger()
307 u32 addr = SDAM_LUT_EN_OFFSET + chan->sdam_offset; in lpg_sdam_configure_triggers()
309 if (!chan->lpg->lpg_chan_sdam) in lpg_sdam_configure_triggers()
312 return nvmem_device_write(chan->lpg->lpg_chan_sdam, addr, 1, &set_trig); in lpg_sdam_configure_triggers()
318 if (!lpg->triled_base) in triled_set()
321 return regmap_update_bits(lpg->map, lpg->triled_base + TRI_LED_EN_CTL, in triled_set()
333 if (len > lpg->lut_size) { in lpg_lut_store_sdam()
334 dev_err(lpg->dev, "Pattern length (%zu) exceeds maximum pattern length (%d)\n", in lpg_lut_store_sdam()
335 len, lpg->lut_size); in lpg_lut_store_sdam()
336 return -EINVAL; in lpg_lut_store_sdam()
339 idx = bitmap_find_next_zero_area(lpg->lut_bitmap, lpg->lut_size, 0, len, 0); in lpg_lut_store_sdam()
340 if (idx >= lpg->lut_size) in lpg_lut_store_sdam()
341 return -ENOSPC; in lpg_lut_store_sdam()
346 if (lpg->lut_sdam) { in lpg_lut_store_sdam()
348 rc = nvmem_device_write(lpg->lut_sdam, addr, 1, &brightness); in lpg_lut_store_sdam()
351 rc = nvmem_device_write(lpg->lpg_chan_sdam, addr, 1, &brightness); in lpg_lut_store_sdam()
358 bitmap_set(lpg->lut_bitmap, idx, len); in lpg_lut_store_sdam()
361 *hi_idx = idx + len - 1; in lpg_lut_store_sdam()
373 idx = bitmap_find_next_zero_area(lpg->lut_bitmap, lpg->lut_size, in lpg_lut_store()
375 if (idx >= lpg->lut_size) in lpg_lut_store()
376 return -ENOMEM; in lpg_lut_store()
381 regmap_bulk_write(lpg->map, lpg->lut_base + LPG_LUT_REG(idx + i), in lpg_lut_store()
385 bitmap_set(lpg->lut_bitmap, idx, len); in lpg_lut_store()
388 *hi_idx = idx + len - 1; in lpg_lut_store()
397 len = hi_idx - lo_idx + 1; in lpg_lut_free()
401 bitmap_clear(lpg->lut_bitmap, lo_idx, len); in lpg_lut_free()
406 if (!lpg->lut_base) in lpg_lut_sync()
409 return regmap_write(lpg->map, lpg->lut_base + RAMP_CONTROL_REG, mask); in lpg_lut_sync()
436 * period = -------------------------- in lpg_calc_freq()
450 if (chan->subtype == LPG_SUBTYPE_HI_RES_PWM) { in lpg_calc_freq()
465 clk_rate_arr[clk_len - 1]); in lpg_calc_freq()
467 return -EINVAL; in lpg_calc_freq()
480 * M = log2 ------------------------------------- in lpg_calc_freq()
505 error = period - actual; in lpg_calc_freq()
517 chan->clk_sel = best_clk; in lpg_calc_freq()
518 chan->pre_div_sel = best_div; in lpg_calc_freq()
519 chan->pre_div_exp = best_m; in lpg_calc_freq()
520 chan->period = best_period; in lpg_calc_freq()
521 chan->pwm_resolution_sel = best_pwm_resolution_sel; in lpg_calc_freq()
531 if (chan->subtype == LPG_SUBTYPE_HI_RES_PWM) { in lpg_calc_duty()
532 max = LPG_RESOLUTION_15BIT - 1; in lpg_calc_duty()
533 clk_rate = lpg_clk_rates_hi_res[chan->clk_sel]; in lpg_calc_duty()
535 max = LPG_RESOLUTION_9BIT - 1; in lpg_calc_duty()
536 clk_rate = lpg_clk_rates[chan->clk_sel]; in lpg_calc_duty()
540 (u64)NSEC_PER_SEC * lpg_pre_divs[chan->pre_div_sel] * (1 << chan->pre_div_exp)); in lpg_calc_duty()
542 chan->pwm_value = min(val, max); in lpg_calc_duty()
548 struct lpg *lpg = chan->lpg; in lpg_apply_freq()
550 if (!chan->enabled) in lpg_apply_freq()
553 val = chan->clk_sel; in lpg_apply_freq()
556 switch (chan->subtype) { in lpg_apply_freq()
564 val |= FIELD_PREP(PWM_SIZE_HI_RES_MASK, chan->pwm_resolution_sel); in lpg_apply_freq()
572 regmap_write(lpg->map, chan->base + LPG_SIZE_CLK_REG, val); in lpg_apply_freq()
574 val = FIELD_PREP(PWM_FREQ_PRE_DIV_MASK, chan->pre_div_sel) | in lpg_apply_freq()
575 FIELD_PREP(PWM_FREQ_EXP_MASK, chan->pre_div_exp); in lpg_apply_freq()
576 regmap_write(lpg->map, chan->base + LPG_PREDIV_CLK_REG, val); in lpg_apply_freq()
583 struct lpg *lpg = chan->lpg; in lpg_enable_glitch()
585 regmap_update_bits(lpg->map, chan->base + PWM_TYPE_CONFIG_REG, in lpg_enable_glitch()
591 struct lpg *lpg = chan->lpg; in lpg_disable_glitch()
593 regmap_update_bits(lpg->map, chan->base + PWM_TYPE_CONFIG_REG, in lpg_disable_glitch()
600 struct lpg *lpg = chan->lpg; in lpg_apply_pwm_value()
601 u16 val = chan->pwm_value; in lpg_apply_pwm_value()
603 if (!chan->enabled) in lpg_apply_pwm_value()
606 regmap_bulk_write(lpg->map, chan->base + PWM_VALUE_REG, &val, sizeof(val)); in lpg_apply_pwm_value()
617 struct nvmem_device *lpg_chan_sdam = chan->lpg->lpg_chan_sdam; in lpg_sdam_apply_lut_control()
618 unsigned int lo_idx = chan->pattern_lo_idx; in lpg_sdam_apply_lut_control()
619 unsigned int hi_idx = chan->pattern_hi_idx; in lpg_sdam_apply_lut_control()
622 struct lpg *lpg = chan->lpg; in lpg_sdam_apply_lut_control()
624 if (!chan->ramp_enabled || chan->pattern_lo_idx == chan->pattern_hi_idx) in lpg_sdam_apply_lut_control()
627 hi_pause = DIV_ROUND_UP(chan->ramp_hi_pause_ms, chan->ramp_tick_ms); in lpg_sdam_apply_lut_control()
628 lo_pause = DIV_ROUND_UP(chan->ramp_lo_pause_ms, chan->ramp_tick_ms); in lpg_sdam_apply_lut_control()
630 if (!chan->ramp_oneshot) in lpg_sdam_apply_lut_control()
632 if (chan->ramp_hi_pause_ms && lpg->lut_sdam) in lpg_sdam_apply_lut_control()
634 if (chan->ramp_lo_pause_ms && lpg->lut_sdam) in lpg_sdam_apply_lut_control()
637 if (lpg->lut_sdam) { in lpg_sdam_apply_lut_control()
638 lut_offset = SDAM_LUT_SDAM_LUT_PATTERN_OFFSET - SDAM_START_BASE; in lpg_sdam_apply_lut_control()
643 …nvmem_device_write(lpg_chan_sdam, SDAM_PBS_SCRATCH_LUT_COUNTER_OFFSET + chan->sdam_offset, 1, &val… in lpg_sdam_apply_lut_control()
644 nvmem_device_write(lpg_chan_sdam, SDAM_PATTERN_CONFIG_OFFSET + chan->sdam_offset, 1, &conf); in lpg_sdam_apply_lut_control()
645 nvmem_device_write(lpg_chan_sdam, SDAM_END_INDEX_OFFSET + chan->sdam_offset, 1, &hi_idx); in lpg_sdam_apply_lut_control()
646 nvmem_device_write(lpg_chan_sdam, SDAM_START_INDEX_OFFSET + chan->sdam_offset, 1, &lo_idx); in lpg_sdam_apply_lut_control()
648 val = RAMP_STEP_DURATION(chan->ramp_tick_ms); in lpg_sdam_apply_lut_control()
651 if (lpg->lut_sdam) { in lpg_sdam_apply_lut_control()
652 …nvmem_device_write(lpg_chan_sdam, SDAM_PAUSE_HI_MULTIPLIER_OFFSET + chan->sdam_offset, 1, &hi_paus… in lpg_sdam_apply_lut_control()
653 …nvmem_device_write(lpg_chan_sdam, SDAM_PAUSE_LO_MULTIPLIER_OFFSET + chan->sdam_offset, 1, &lo_paus… in lpg_sdam_apply_lut_control()
660 struct lpg *lpg = chan->lpg; in lpg_apply_lut_control()
664 unsigned int lo_idx = chan->pattern_lo_idx; in lpg_apply_lut_control()
665 unsigned int hi_idx = chan->pattern_hi_idx; in lpg_apply_lut_control()
666 u16 step = chan->ramp_tick_ms; in lpg_apply_lut_control()
668 if (!chan->ramp_enabled || chan->pattern_lo_idx == chan->pattern_hi_idx) in lpg_apply_lut_control()
671 hi_pause = DIV_ROUND_UP(chan->ramp_hi_pause_ms, step); in lpg_apply_lut_control()
672 lo_pause = DIV_ROUND_UP(chan->ramp_lo_pause_ms, step); in lpg_apply_lut_control()
674 if (!chan->ramp_reverse) in lpg_apply_lut_control()
676 if (!chan->ramp_oneshot) in lpg_apply_lut_control()
678 if (chan->ramp_ping_pong) in lpg_apply_lut_control()
680 if (chan->ramp_hi_pause_ms) in lpg_apply_lut_control()
682 if (chan->ramp_lo_pause_ms) in lpg_apply_lut_control()
685 regmap_write(lpg->map, chan->base + LPG_PATTERN_CONFIG_REG, conf); in lpg_apply_lut_control()
686 regmap_write(lpg->map, chan->base + LPG_HI_IDX_REG, hi_idx); in lpg_apply_lut_control()
687 regmap_write(lpg->map, chan->base + LPG_LO_IDX_REG, lo_idx); in lpg_apply_lut_control()
689 regmap_bulk_write(lpg->map, chan->base + LPG_RAMP_DURATION_REG, &step, sizeof(step)); in lpg_apply_lut_control()
690 regmap_write(lpg->map, chan->base + LPG_HI_PAUSE_REG, hi_pause); in lpg_apply_lut_control()
691 regmap_write(lpg->map, chan->base + LPG_LO_PAUSE_REG, lo_pause); in lpg_apply_lut_control()
702 struct lpg *lpg = chan->lpg; in lpg_apply_control()
706 if (chan->enabled) in lpg_apply_control()
709 if (chan->pattern_lo_idx != chan->pattern_hi_idx) in lpg_apply_control()
714 regmap_write(lpg->map, chan->base + PWM_ENABLE_CONTROL_REG, ctrl); in lpg_apply_control()
720 if (chan->enabled) in lpg_apply_control()
728 struct lpg *lpg = chan->lpg; in lpg_apply_sync()
730 regmap_write(lpg->map, chan->base + PWM_SYNC_REG, LPG_SYNC_PWM); in lpg_apply_sync()
736 struct device_node *np = lpg->dev->of_node; in lpg_parse_dtest()
742 if (count == -EINVAL) { in lpg_parse_dtest()
747 } else if (count != lpg->data->num_channels * 2) { in lpg_parse_dtest()
748 return dev_err_probe(lpg->dev, -EINVAL, in lpg_parse_dtest()
750 lpg->data->num_channels * 2); in lpg_parse_dtest()
753 for (i = 0; i < lpg->data->num_channels; i++) { in lpg_parse_dtest()
754 chan = &lpg->channels[i]; in lpg_parse_dtest()
757 &chan->dtest_line); in lpg_parse_dtest()
762 &chan->dtest_value); in lpg_parse_dtest()
770 return dev_err_probe(lpg->dev, ret, "malformed qcom,dtest\n"); in lpg_parse_dtest()
775 struct lpg *lpg = chan->lpg; in lpg_apply_dtest()
777 if (!chan->dtest_line) in lpg_apply_dtest()
780 regmap_write(lpg->map, chan->base + PWM_SEC_ACCESS_REG, 0xa5); in lpg_apply_dtest()
781 regmap_write(lpg->map, chan->base + PWM_DTEST_REG(chan->dtest_line), in lpg_apply_dtest()
782 chan->dtest_value); in lpg_apply_dtest()
792 if (chan->lpg->lpg_chan_sdam) in lpg_apply()
808 struct lpg *lpg = led->lpg; in lpg_brightness_set()
811 for (i = 0; i < led->num_channels; i++) { in lpg_brightness_set()
812 chan = led->channels[i]; in lpg_brightness_set()
816 chan->enabled = false; in lpg_brightness_set()
817 chan->ramp_enabled = false; in lpg_brightness_set()
818 } else if (chan->pattern_lo_idx != chan->pattern_hi_idx) { in lpg_brightness_set()
822 chan->enabled = true; in lpg_brightness_set()
823 chan->ramp_enabled = true; in lpg_brightness_set()
825 lut_mask |= chan->lut_mask; in lpg_brightness_set()
826 triled_enabled |= chan->triled_mask; in lpg_brightness_set()
830 duty = div_u64(brightness * chan->period, cdev->max_brightness); in lpg_brightness_set()
832 chan->enabled = true; in lpg_brightness_set()
833 chan->ramp_enabled = false; in lpg_brightness_set()
835 triled_enabled |= chan->triled_mask; in lpg_brightness_set()
838 triled_mask |= chan->triled_mask; in lpg_brightness_set()
860 mutex_lock(&led->lpg->lock); in lpg_brightness_single_set()
865 mutex_unlock(&led->lpg->lock); in lpg_brightness_single_set()
876 mutex_lock(&led->lpg->lock); in lpg_brightness_mc_set()
879 lpg_brightness_set(led, cdev, mc->subled_info); in lpg_brightness_mc_set()
881 mutex_unlock(&led->lpg->lock); in lpg_brightness_mc_set()
892 struct lpg *lpg = led->lpg; in lpg_blink_set()
904 for (i = 0; i < led->num_channels; i++) { in lpg_blink_set()
905 chan = led->channels[i]; in lpg_blink_set()
910 chan->enabled = true; in lpg_blink_set()
911 chan->ramp_enabled = false; in lpg_blink_set()
913 triled_mask |= chan->triled_mask; in lpg_blink_set()
921 chan = led->channels[0]; in lpg_blink_set()
922 duty = div_u64(chan->pwm_value * chan->period, LPG_RESOLUTION_9BIT); in lpg_blink_set()
924 *delay_off = div_u64(chan->period - duty, NSEC_PER_MSEC); in lpg_blink_set()
935 mutex_lock(&led->lpg->lock); in lpg_blink_single_set()
939 mutex_unlock(&led->lpg->lock); in lpg_blink_single_set()
951 mutex_lock(&led->lpg->lock); in lpg_blink_mc_set()
955 mutex_unlock(&led->lpg->lock); in lpg_blink_mc_set()
964 struct lpg *lpg = led->lpg; in lpg_pattern_set()
976 int ret = -EINVAL; in lpg_pattern_set()
979 if (repeat != -1 && repeat != 1) in lpg_pattern_set()
980 return -EINVAL; in lpg_pattern_set()
983 * The standardized leds-trigger-pattern format defines that the in lpg_pattern_set()
986 * describes that the way to perform instant transitions a zero-length in lpg_pattern_set()
991 * a zero-length transition. in lpg_pattern_set()
994 return -EINVAL; in lpg_pattern_set()
998 return -ENOMEM; in lpg_pattern_set()
1038 * SDAM-based devices do not support "ping pong", and only supports in lpg_pattern_set()
1043 if (lpg->lut_base) { in lpg_pattern_set()
1046 brightness_b = pattern[len - i - 1].brightness; in lpg_pattern_set()
1073 if (i != actual_len - 1) in lpg_pattern_set()
1084 * SDAM-based devices without dedicated LUT SDAM require equal in lpg_pattern_set()
1087 if (lpg->lut_base || lpg->lut_sdam) { in lpg_pattern_set()
1089 hi_pause = pattern[actual_len - 1].delta_t; in lpg_pattern_set()
1091 if (delta_t != pattern[0].delta_t || delta_t != pattern[actual_len - 1].delta_t) in lpg_pattern_set()
1096 mutex_lock(&lpg->lock); in lpg_pattern_set()
1098 if (lpg->lut_base) in lpg_pattern_set()
1106 for (i = 0; i < led->num_channels; i++) { in lpg_pattern_set()
1107 chan = led->channels[i]; in lpg_pattern_set()
1109 chan->ramp_tick_ms = delta_t; in lpg_pattern_set()
1110 chan->ramp_ping_pong = ping_pong; in lpg_pattern_set()
1111 chan->ramp_oneshot = repeat != -1; in lpg_pattern_set()
1113 chan->ramp_lo_pause_ms = lo_pause; in lpg_pattern_set()
1114 chan->ramp_hi_pause_ms = hi_pause; in lpg_pattern_set()
1116 chan->pattern_lo_idx = lo_idx; in lpg_pattern_set()
1117 chan->pattern_hi_idx = hi_idx; in lpg_pattern_set()
1121 mutex_unlock(&lpg->lock); in lpg_pattern_set()
1153 for (i = 0; i < led->num_channels; i++) in lpg_pattern_mc_set()
1154 triled_mask |= led->channels[i]->triled_mask; in lpg_pattern_mc_set()
1155 triled_set(led->lpg, triled_mask, 0); in lpg_pattern_mc_set()
1162 lpg_brightness_set(led, cdev, mc->subled_info); in lpg_pattern_mc_set()
1170 struct lpg *lpg = led->lpg; in lpg_pattern_clear()
1173 mutex_lock(&lpg->lock); in lpg_pattern_clear()
1175 chan = led->channels[0]; in lpg_pattern_clear()
1176 lpg_lut_free(lpg, chan->pattern_lo_idx, chan->pattern_hi_idx); in lpg_pattern_clear()
1178 for (i = 0; i < led->num_channels; i++) { in lpg_pattern_clear()
1179 chan = led->channels[i]; in lpg_pattern_clear()
1181 lpg_clear_pbs_trigger(chan->lpg, chan->lut_mask); in lpg_pattern_clear()
1182 chan->pattern_lo_idx = 0; in lpg_pattern_clear()
1183 chan->pattern_hi_idx = 0; in lpg_pattern_clear()
1186 mutex_unlock(&lpg->lock); in lpg_pattern_clear()
1214 struct lpg_channel *chan = &lpg->channels[pwm->hwpwm]; in lpg_pwm_request()
1216 return chan->in_use ? -EBUSY : 0; in lpg_pwm_request()
1221 * - Updating both duty and period is not done atomically, so the output signal
1223 * - Changed parameters takes effect immediately.
1224 * - A disabled channel outputs a logical 0.
1230 struct lpg_channel *chan = &lpg->channels[pwm->hwpwm]; in lpg_pwm_apply()
1233 if (state->polarity != PWM_POLARITY_NORMAL) in lpg_pwm_apply()
1234 return -EINVAL; in lpg_pwm_apply()
1236 mutex_lock(&lpg->lock); in lpg_pwm_apply()
1238 if (state->enabled) { in lpg_pwm_apply()
1239 ret = lpg_calc_freq(chan, state->period); in lpg_pwm_apply()
1243 lpg_calc_duty(chan, state->duty_cycle); in lpg_pwm_apply()
1245 chan->enabled = state->enabled; in lpg_pwm_apply()
1249 triled_set(lpg, chan->triled_mask, chan->enabled ? chan->triled_mask : 0); in lpg_pwm_apply()
1252 mutex_unlock(&lpg->lock); in lpg_pwm_apply()
1261 struct lpg_channel *chan = &lpg->channels[pwm->hwpwm]; in lpg_pwm_get_state()
1270 ret = regmap_read(lpg->map, chan->base + LPG_SIZE_CLK_REG, &val); in lpg_pwm_get_state()
1274 if (chan->subtype == LPG_SUBTYPE_HI_RES_PWM) { in lpg_pwm_get_state()
1283 ret = regmap_read(lpg->map, chan->base + LPG_PREDIV_CLK_REG, &val); in lpg_pwm_get_state()
1290 ret = regmap_bulk_read(lpg->map, chan->base + PWM_VALUE_REG, &pwm_value, sizeof(pwm_value)); in lpg_pwm_get_state()
1294 state->period = DIV_ROUND_UP_ULL((u64)NSEC_PER_SEC * (1 << resolution) * in lpg_pwm_get_state()
1296 state->duty_cycle = DIV_ROUND_UP_ULL((u64)NSEC_PER_SEC * pwm_value * pre_div * (1 << m), refclk); in lpg_pwm_get_state()
1298 state->period = 0; in lpg_pwm_get_state()
1299 state->duty_cycle = 0; in lpg_pwm_get_state()
1302 ret = regmap_read(lpg->map, chan->base + PWM_ENABLE_CONTROL_REG, &val); in lpg_pwm_get_state()
1306 state->enabled = FIELD_GET(LPG_ENABLE_CONTROL_OUTPUT, val); in lpg_pwm_get_state()
1307 state->polarity = PWM_POLARITY_NORMAL; in lpg_pwm_get_state()
1309 if (state->duty_cycle > state->period) in lpg_pwm_get_state()
1310 state->duty_cycle = state->period; in lpg_pwm_get_state()
1326 lpg->pwm = chip = devm_pwmchip_alloc(lpg->dev, lpg->num_channels, 0); in lpg_add_pwm()
1330 chip->ops = &lpg_pwm_ops; in lpg_add_pwm()
1333 ret = devm_pwmchip_add(lpg->dev, chip); in lpg_add_pwm()
1335 dev_err_probe(lpg->dev, ret, "failed to add PWM chip\n"); in lpg_add_pwm()
1349 if (ret || !reg || reg > lpg->num_channels) in lpg_parse_channel()
1350 return dev_err_probe(lpg->dev, -EINVAL, "invalid \"reg\" of %pOFn\n", np); in lpg_parse_channel()
1352 chan = &lpg->channels[reg - 1]; in lpg_parse_channel()
1353 chan->in_use = true; in lpg_parse_channel()
1356 if (ret < 0 && ret != -EINVAL) in lpg_parse_channel()
1357 return dev_err_probe(lpg->dev, ret, in lpg_parse_channel()
1360 chan->color = color; in lpg_parse_channel()
1380 if (ret < 0 && ret != -EINVAL) in lpg_add_led()
1381 return dev_err_probe(lpg->dev, ret, in lpg_add_led()
1389 led = devm_kzalloc(lpg->dev, struct_size(led, channels, num_channels), GFP_KERNEL); in lpg_add_led()
1391 return -ENOMEM; in lpg_add_led()
1393 led->lpg = lpg; in lpg_add_led()
1394 led->num_channels = num_channels; in lpg_add_led()
1397 info = devm_kcalloc(lpg->dev, num_channels, sizeof(*info), GFP_KERNEL); in lpg_add_led()
1399 return -ENOMEM; in lpg_add_led()
1402 ret = lpg_parse_channel(lpg, child, &led->channels[i]); in lpg_add_led()
1406 info[i].color_index = led->channels[i]->color; in lpg_add_led()
1411 led->mcdev.subled_info = info; in lpg_add_led()
1412 led->mcdev.num_colors = num_channels; in lpg_add_led()
1414 cdev = &led->mcdev.led_cdev; in lpg_add_led()
1415 cdev->brightness_set_blocking = lpg_brightness_mc_set; in lpg_add_led()
1416 cdev->blink_set = lpg_blink_mc_set; in lpg_add_led()
1419 if (lpg->lut_base || lpg->lpg_chan_sdam) { in lpg_add_led()
1420 cdev->pattern_set = lpg_pattern_mc_set; in lpg_add_led()
1421 cdev->pattern_clear = lpg_pattern_mc_clear; in lpg_add_led()
1424 ret = lpg_parse_channel(lpg, np, &led->channels[0]); in lpg_add_led()
1428 cdev = &led->cdev; in lpg_add_led()
1429 cdev->brightness_set_blocking = lpg_brightness_single_set; in lpg_add_led()
1430 cdev->blink_set = lpg_blink_single_set; in lpg_add_led()
1433 if (lpg->lut_base || lpg->lpg_chan_sdam) { in lpg_add_led()
1434 cdev->pattern_set = lpg_pattern_single_set; in lpg_add_led()
1435 cdev->pattern_clear = lpg_pattern_single_clear; in lpg_add_led()
1439 cdev->default_trigger = of_get_property(np, "linux,default-trigger", NULL); in lpg_add_led()
1441 if (lpg->lpg_chan_sdam) in lpg_add_led()
1442 cdev->max_brightness = PPG_MAX_LED_BRIGHTNESS; in lpg_add_led()
1444 cdev->max_brightness = LPG_RESOLUTION_9BIT - 1; in lpg_add_led()
1446 if (!of_property_read_string(np, "default-state", &state) && in lpg_add_led()
1448 cdev->brightness = cdev->max_brightness; in lpg_add_led()
1450 cdev->brightness = LED_OFF; in lpg_add_led()
1452 cdev->brightness_set_blocking(cdev, cdev->brightness); in lpg_add_led()
1457 ret = devm_led_classdev_multicolor_register_ext(lpg->dev, &led->mcdev, &init_data); in lpg_add_led()
1459 ret = devm_led_classdev_register_ext(lpg->dev, &led->cdev, &init_data); in lpg_add_led()
1461 dev_err_probe(lpg->dev, ret, "unable to register %s\n", cdev->name); in lpg_add_led()
1468 const struct lpg_data *data = lpg->data; in lpg_init_channels()
1472 lpg->num_channels = data->num_channels; in lpg_init_channels()
1473 lpg->channels = devm_kcalloc(lpg->dev, data->num_channels, in lpg_init_channels()
1475 if (!lpg->channels) in lpg_init_channels()
1476 return -ENOMEM; in lpg_init_channels()
1478 for (i = 0; i < data->num_channels; i++) { in lpg_init_channels()
1479 chan = &lpg->channels[i]; in lpg_init_channels()
1481 chan->lpg = lpg; in lpg_init_channels()
1482 chan->base = data->channels[i].base; in lpg_init_channels()
1483 chan->triled_mask = data->channels[i].triled_mask; in lpg_init_channels()
1484 chan->lut_mask = BIT(i); in lpg_init_channels()
1485 chan->sdam_offset = data->channels[i].sdam_offset; in lpg_init_channels()
1487 regmap_read(lpg->map, chan->base + LPG_SUBTYPE_REG, &chan->subtype); in lpg_init_channels()
1495 struct device_node *np = lpg->dev->of_node; in lpg_init_triled()
1499 if (!lpg->data->triled_base) in lpg_init_triled()
1502 lpg->triled_base = lpg->data->triled_base; in lpg_init_triled()
1503 lpg->triled_has_atc_ctl = lpg->data->triled_has_atc_ctl; in lpg_init_triled()
1504 lpg->triled_has_src_sel = lpg->data->triled_has_src_sel; in lpg_init_triled()
1506 if (lpg->triled_has_src_sel) { in lpg_init_triled()
1507 ret = of_property_read_u32(np, "qcom,power-source", &lpg->triled_src); in lpg_init_triled()
1508 if (ret || lpg->triled_src == 2 || lpg->triled_src > 3) in lpg_init_triled()
1509 return dev_err_probe(lpg->dev, -EINVAL, in lpg_init_triled()
1514 if (lpg->triled_has_atc_ctl) in lpg_init_triled()
1515 regmap_write(lpg->map, lpg->triled_base + TRI_LED_ATC_CTL, 0); in lpg_init_triled()
1518 if (lpg->triled_has_src_sel) in lpg_init_triled()
1519 regmap_write(lpg->map, lpg->triled_base + TRI_LED_SRC_SEL, lpg->triled_src); in lpg_init_triled()
1522 regmap_write(lpg->map, lpg->triled_base + TRI_LED_EN_CTL, 0); in lpg_init_triled()
1529 const struct lpg_data *data = lpg->data; in lpg_init_lut()
1531 if (!data->lut_size) in lpg_init_lut()
1534 lpg->lut_size = data->lut_size; in lpg_init_lut()
1535 if (data->lut_base) in lpg_init_lut()
1536 lpg->lut_base = data->lut_base; in lpg_init_lut()
1538 lpg->lut_bitmap = devm_bitmap_zalloc(lpg->dev, lpg->lut_size, GFP_KERNEL); in lpg_init_lut()
1539 if (!lpg->lut_bitmap) in lpg_init_lut()
1540 return -ENOMEM; in lpg_init_lut()
1550 sdam_count = of_property_count_strings(lpg->dev->of_node, "nvmem-names"); in lpg_init_sdam()
1554 return -EINVAL; in lpg_init_sdam()
1557 lpg->lpg_chan_sdam = devm_nvmem_device_get(lpg->dev, "lpg_chan_sdam"); in lpg_init_sdam()
1558 if (IS_ERR(lpg->lpg_chan_sdam)) in lpg_init_sdam()
1559 return dev_err_probe(lpg->dev, PTR_ERR(lpg->lpg_chan_sdam), in lpg_init_sdam()
1563 /* Get PBS device node if single SDAM device */ in lpg_init_sdam()
1564 lpg->pbs_dev = get_pbs_client_device(lpg->dev); in lpg_init_sdam()
1565 if (IS_ERR(lpg->pbs_dev)) in lpg_init_sdam()
1566 return dev_err_probe(lpg->dev, PTR_ERR(lpg->pbs_dev), in lpg_init_sdam()
1567 "Failed to get PBS client device\n"); in lpg_init_sdam()
1570 lpg->lut_sdam = devm_nvmem_device_get(lpg->dev, "lut_sdam"); in lpg_init_sdam()
1571 if (IS_ERR(lpg->lut_sdam)) in lpg_init_sdam()
1572 return dev_err_probe(lpg->dev, PTR_ERR(lpg->lut_sdam), in lpg_init_sdam()
1576 for (i = 0; i < lpg->num_channels; i++) { in lpg_init_sdam()
1577 struct lpg_channel *chan = &lpg->channels[i]; in lpg_init_sdam()
1579 if (chan->sdam_offset) { in lpg_init_sdam()
1580 rc = nvmem_device_write(lpg->lpg_chan_sdam, in lpg_init_sdam()
1581 SDAM_PBS_SCRATCH_LUT_COUNTER_OFFSET + chan->sdam_offset, 1, &val); in lpg_init_sdam()
1589 rc = lpg_clear_pbs_trigger(chan->lpg, chan->lut_mask); in lpg_init_sdam()
1604 lpg = devm_kzalloc(&pdev->dev, sizeof(*lpg), GFP_KERNEL); in lpg_probe()
1606 return -ENOMEM; in lpg_probe()
1608 lpg->data = of_device_get_match_data(&pdev->dev); in lpg_probe()
1609 if (!lpg->data) in lpg_probe()
1610 return -EINVAL; in lpg_probe()
1612 lpg->dev = &pdev->dev; in lpg_probe()
1613 mutex_init(&lpg->lock); in lpg_probe()
1615 lpg->map = dev_get_regmap(pdev->dev.parent, NULL); in lpg_probe()
1616 if (!lpg->map) in lpg_probe()
1617 return dev_err_probe(&pdev->dev, -ENXIO, "parent regmap unavailable\n"); in lpg_probe()
1639 for_each_available_child_of_node_scoped(pdev->dev.of_node, np) { in lpg_probe()
1645 for (i = 0; i < lpg->num_channels; i++) in lpg_probe()
1646 lpg_apply_dtest(&lpg->channels[i]); in lpg_probe()
1718 /* PMI632 uses SDAM instead of LUT for pattern */
1821 { .compatible = "qcom,pm660l-lpg", .data = &pm660l_lpg_data },
1822 { .compatible = "qcom,pm8150b-lpg", .data = &pm8150b_lpg_data },
1823 { .compatible = "qcom,pm8150l-lpg", .data = &pm8150l_lpg_data },
1824 { .compatible = "qcom,pm8350c-pwm", .data = &pm8350c_pwm_data },
1825 { .compatible = "qcom,pm8916-pwm", .data = &pm8916_pwm_data },
1826 { .compatible = "qcom,pm8941-lpg", .data = &pm8941_lpg_data },
1827 { .compatible = "qcom,pm8994-lpg", .data = &pm8994_lpg_data },
1828 { .compatible = "qcom,pmi632-lpg", .data = &pmi632_lpg_data },
1829 { .compatible = "qcom,pmi8950-pwm", .data = &pmi8950_pwm_data },
1830 { .compatible = "qcom,pmi8994-lpg", .data = &pmi8994_lpg_data },
1831 { .compatible = "qcom,pmi8998-lpg", .data = &pmi8998_lpg_data },
1832 { .compatible = "qcom,pmc8180c-lpg", .data = &pm8150l_lpg_data },
1833 { .compatible = "qcom,pmk8550-pwm", .data = &pmk8550_pwm_data },
1841 .name = "qcom-spmi-lpg",