From f8b640fb2d587eb8b29475158f0ce51d585cadf3 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Wed, 15 Aug 2018 15:11:09 +0800 Subject: [PATCH] pwm: pwm-qti-lpg: Add property for LPG channel synchronization Add qcom,sync-channel-ids property for grouping the LPG channels which are expected to be enabled with a synchronized pattern. Change-Id: I1f5d83f2e6094771cb30d567d9d4d4476f1f3dc9 Signed-off-by: Fenglin Wu --- .../devicetree/bindings/pwm/pwm-qti-lpg.txt | 12 +++ drivers/pwm/pwm-qti-lpg.c | 76 ++++++++++++++++++- 2 files changed, 86 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt b/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt index ddd90e134a16..d01f25a190c1 100644 --- a/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt +++ b/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt @@ -40,6 +40,17 @@ device module in Qualcomm Technologies, Inc. PMIC chips. channels. This property is required if any LPG channels support LUT mode. +- qcom,sync-channel-ids: + Usage: optional + Value type: + Definition: The hardware IDs of the LPG channel that required be + grouped together. These channels will share the same LUT + ramping configuration so that they will be enabled with a + synchronized pattern. If the LUT ramping configuration + differs for the channels grouped for synchronization, + configuration of the first channel will be applied for + all others. + Subnode is optional if LUT mode is not required, it's required if any LPG channels expected to be supported in LUT mode. @@ -114,6 +125,7 @@ Example: #pwm-cells = <2>; qcom,lut-patterns = <0 14 28 42 56 70 84 100 100 84 70 56 42 28 14 0>; + qcom,sync-channel-ids = <3 4 5>; lpg@3 { qcom,lpg-chan-id = <3>; qcom,ramp-step-ms = <200>; diff --git a/drivers/pwm/pwm-qti-lpg.c b/drivers/pwm/pwm-qti-lpg.c index aca41a8d5b9c..784fc8bb3ae2 100644 --- a/drivers/pwm/pwm-qti-lpg.c +++ b/drivers/pwm/pwm-qti-lpg.c @@ -165,6 +165,7 @@ struct qpnp_lpg_chip { struct qpnp_lpg_channel *lpgs; struct qpnp_lpg_lut *lut; struct mutex bus_lock; + u32 *lpg_group; u32 num_lpgs; }; @@ -659,8 +660,9 @@ static int qpnp_lpg_pwm_src_enable(struct qpnp_lpg_channel *lpg, bool en) { struct qpnp_lpg_chip *chip = lpg->chip; struct qpnp_lpg_lut *lut = chip->lut; + struct pwm_device *pwm; u8 mask, val; - int rc; + int i, lpg_idx, rc; mask = LPG_PWM_SRC_SELECT_MASK | LPG_EN_LPG_OUT_BIT | LPG_EN_RAMP_GEN_MASK; @@ -680,8 +682,31 @@ static int qpnp_lpg_pwm_src_enable(struct qpnp_lpg_channel *lpg, bool en) } if (lpg->src_sel == LUT_PATTERN && en) { - mutex_lock(&lut->lock); val = 1 << lpg->lpg_idx; + for (i = 0; i < chip->num_lpgs; i++) { + if (chip->lpg_group == NULL) + break; + if (chip->lpg_group[i] == 0) + break; + lpg_idx = chip->lpg_group[i] - 1; + pwm = &chip->pwm_chip.pwms[lpg_idx]; + if ((pwm_get_output_type(pwm) == PWM_OUTPUT_MODULATED) + && pwm_is_enabled(pwm)) { + rc = qpnp_lpg_masked_write(&chip->lpgs[lpg_idx], + REG_LPG_ENABLE_CONTROL, + LPG_EN_LPG_OUT_BIT, 0); + if (rc < 0) + break; + rc = qpnp_lpg_masked_write(&chip->lpgs[lpg_idx], + REG_LPG_ENABLE_CONTROL, + LPG_EN_LPG_OUT_BIT, + LPG_EN_LPG_OUT_BIT); + if (rc < 0) + break; + val |= 1 << lpg_idx; + } + } + mutex_lock(&lut->lock); rc = qpnp_lut_write(lut, REG_LPG_LUT_RAMP_CONTROL, val); if (rc < 0) dev_err(chip->dev, "Write LPG_LUT_RAMP_CONTROL failed, rc=%d\n", @@ -1146,6 +1171,53 @@ static int qpnp_lpg_parse_dt(struct qpnp_lpg_chip *chip) "qcom,ramp-toggle"); } + rc = of_property_count_elems_of_size(chip->dev->of_node, + "qcom,sync-channel-ids", sizeof(u32)); + if (rc < 0) + return 0; + + length = rc; + if (length > chip->num_lpgs) { + dev_err(chip->dev, "qcom,sync-channel-ids has too many channels: %d\n", + length); + return -EINVAL; + } + + chip->lpg_group = devm_kcalloc(chip->dev, chip->num_lpgs, + sizeof(u32), GFP_KERNEL); + if (!chip->lpg_group) + return -ENOMEM; + + rc = of_property_read_u32_array(chip->dev->of_node, + "qcom,sync-channel-ids", chip->lpg_group, length); + if (rc < 0) { + dev_err(chip->dev, "Get qcom,sync-channel-ids failed, rc=%d\n", + rc); + return rc; + } + + for (i = 0; i < length; i++) { + if (chip->lpg_group[i] <= 0 || + chip->lpg_group[i] > chip->num_lpgs) { + dev_err(chip->dev, "lpg_group[%d]: %d is not a valid channel\n", + i, chip->lpg_group[i]); + return -EINVAL; + } + } + + /* + * The LPG channel in the same group should have the same ramping + * configuration, so force to use the ramping configuration of the + * 1st LPG channel in the group for sychronization. + */ + lpg = &chip->lpgs[chip->lpg_group[0] - 1]; + ramp = &lpg->ramp_config; + + for (i = 1; i < length; i++) { + lpg = &chip->lpgs[chip->lpg_group[i] - 1]; + memcpy(&lpg->ramp_config, ramp, sizeof(struct lpg_ramp_config)); + } + return 0; }