diff --git a/drivers/pwm/pwm-qpnp.c b/drivers/pwm/pwm-qpnp.c index 38b70e4e6edd..fecec1e2738c 100644 --- a/drivers/pwm/pwm-qpnp.c +++ b/drivers/pwm/pwm-qpnp.c @@ -1139,6 +1139,88 @@ static int qpnp_lpg_configure_lut_state(struct qpnp_pwm_chip *chip, return rc; } +static int qpnp_lpg_configure_lut_states(struct qpnp_pwm_chip **chips, + int num, enum qpnp_lut_state state) +{ + struct qpnp_pwm_chip *chip; + struct qpnp_lpg_config *lpg_config; + u8 value1, value2, mask1, mask2; + u8 *reg1, *reg2; + u16 addr, addr1; + int rc, i; + bool test_enable; + u8 ramp_en = 0, ramp_mask = 0; + + + for (i = 0; i < num; i++) { + chip = chips[i]; + lpg_config = &chip->lpg_config; + value1 = chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL]; + reg1 = &chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL]; + reg2 = &chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL]; + mask2 = QPNP_EN_PWM_HIGH_MASK | QPNP_EN_PWM_LO_MASK | + QPNP_PWM_SRC_SELECT_MASK | QPNP_PWM_EN_RAMP_GEN_MASK; + if (chip->sub_type != QPNP_LPG_S_CHAN_SUB_TYPE) + mask2 |= QPNP_EN_PWM_OUTPUT_MASK; + + if (chip->sub_type == QPNP_LPG_CHAN_SUB_TYPE + && chip->revision == QPNP_LPG_REVISION_0) { + if (state == QPNP_LUT_ENABLE) { + QPNP_ENABLE_LUT_V0(value1); + value2 = QPNP_ENABLE_LPG_MODE(chip); + } else { + QPNP_DISABLE_LUT_V0(value1); + value2 = QPNP_DISABLE_LPG_MODE(chip); + } + mask1 = QPNP_RAMP_START_MASK; + addr1 = SPMI_LPG_REG_ADDR(lpg_config->base_addr, + QPNP_RAMP_CONTROL); + } else if ((chip->sub_type == QPNP_LPG_CHAN_SUB_TYPE + && chip->revision == QPNP_LPG_REVISION_1) + || chip->sub_type == QPNP_LPG_S_CHAN_SUB_TYPE) { + if (state == QPNP_LUT_ENABLE) { + QPNP_ENABLE_LUT_V1(value1, + lpg_config->lut_config.ramp_index); + value2 = QPNP_ENABLE_LPG_MODE(chip); + } else { + value2 = QPNP_DISABLE_LPG_MODE(chip); + } + mask1 = value1; + addr1 = lpg_config->lut_base_addr + + SPMI_LPG_REV1_RAMP_CONTROL_OFFSET; + } else { + pr_err("Unsupported LPG subtype 0x%02x, revision 0x%02x\n", + chip->sub_type, chip->revision); + return -EINVAL; + } + + addr = SPMI_LPG_REG_ADDR(lpg_config->base_addr, + QPNP_ENABLE_CONTROL); + + if (chip->in_test_mode) { + test_enable = (state == QPNP_LUT_ENABLE) ? 1 : 0; + rc = qpnp_dtest_config(chip, test_enable); + if (rc) + pr_err("Failed to configure TEST mode\n"); + } + + rc = qpnp_lpg_save_and_write(value2, mask2, reg2, + addr, 1, chip); + if (rc) + return rc; + + ramp_en |= value1; + ramp_mask |= mask1; + } + + if (state == QPNP_LUT_ENABLE + || (chip->sub_type == QPNP_LPG_CHAN_SUB_TYPE + && chip->revision == QPNP_LPG_REVISION_0)) + rc = qpnp_lpg_save_and_write(ramp_en, ramp_mask, reg1, + addr1, 1, chip); + return rc; +} + static inline int qpnp_enable_pwm_mode(struct qpnp_pwm_chip *chip) { if (chip->pwm_config.supported_sizes == QPNP_PWM_SIZE_7_8_BIT) @@ -1443,6 +1525,49 @@ static int qpnp_pwm_enable(struct pwm_chip *pwm_chip, return rc; } +int pwm_enable_synchronized(struct pwm_device **pwms, size_t num) +{ + unsigned long *flags; + struct qpnp_pwm_chip **chips; + int rc = 0, i; + + if (pwms == NULL || IS_ERR(pwms) || num == 0) { + pr_err("Invalid pwm handle or idx_len=0\n"); + return -EINVAL; + } + + flags = kzalloc(sizeof(unsigned long) * num, GFP_KERNEL); + if (!flags) + return -ENOMEM; + + chips = kzalloc(sizeof(struct qpnp_pwm_chip *) * num, GFP_KERNEL); + if (!chips) { + kfree(flags); + return -ENOMEM; + } + + for (i = 0; i < num; i++) { + chips[i] = qpnp_pwm_from_pwm_dev(pwms[i]); + if (chips[i] == NULL) { + rc = -EINVAL; + goto err_failed; + } + spin_lock_irqsave(&chips[i]->lpg_lock, flags[i]); + } + + rc = qpnp_lpg_configure_lut_states(chips, num, QPNP_LUT_ENABLE); + +err_failed: + while(i > 0) { + spin_unlock_irqrestore(&chips[i - 1]->lpg_lock, flags[i - 1]); + i--; + } + kfree(flags); + kfree(chips); + return rc; +} +EXPORT_SYMBOL_GPL(pwm_enable_synchronized); + /** * qpnp_pwm_disable - stop a PWM output toggling * @pwm_chip: the PWM chip diff --git a/include/linux/qpnp/pwm.h b/include/linux/qpnp/pwm.h index 020f18b2cc4b..275b12358128 100644 --- a/include/linux/qpnp/pwm.h +++ b/include/linux/qpnp/pwm.h @@ -185,6 +185,11 @@ static inline int pwm_config_us(struct pwm_device *pwm, } #endif +/* + * synchronized enable of multiple pwm instances + */ +int pwm_enable_synchronized(struct pwm_device **pwms, size_t num); + /* Standard APIs supported */ /* * pwm_request - request a PWM device