Loading Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt +118 −7 Original line number Diff line number Diff line Loading @@ -11,26 +11,137 @@ device module in Qualcomm Technologies, Inc. PMIC chips. - reg: Usage: required Value type: <prop-encoded-array> Definition: Register base and length for LPG modules. The length varies based on the number of channels available in the PMIC chips. Definition: Register base and length for LPG and LUT modules. LPG size or length available per channel varies depending on the number of channels in PMIC. - reg-names: Usage: required Value type: <string> Definition: The name of the register defined in the reg property. It must be "lpg-base". It must have "lpg-base", "lut-base" is optional but it's required if any LPG channels support LUT mode. - #pwm-cells: Usage: required Value type: <u32> Definition: See Documentation/devicetree/bindings/pwm/pwm.txt; Definition: The number of cells in "pwms" property specified in PWM user nodes. It should be 2. The first cell is the PWM channel ID indexed from 0, and the second cell is the PWM default period in nanoseconds. - qcom,lut-patterns: Usage: optional Value type: <prop-encoded-array> Definition: Duty ratios in percentages for LPG working at LUT mode. These duty ratios will be translated into PWM values and stored in LUT module. The LUT module has resource to store 47 PWM values at max and shared for all LPG channels. This property is required if any LPG channels support LUT mode. Subnode is optional if LUT mode is not required, it's required if any LPG channels expected to be supported in LUT mode. Subnode properties: Subnodes for each LPG channel (lpg@X) can be defined if any of the following parameters needs to be configured for that channel. - qcom,lpg-chan-id: Usage: required Value type: <u32> Definition: The LPG channel's hardware ID indexed from 1. Allowed range is 1 - 8. Maximum value depends on the number of channels supported on PMIC. - qcom,ramp-step-ms: Usage: required Value type: <u32> Definition: The step duration in milliseconds for LPG staying at each duty specified in the LUT pattern. Allowed range is 1 - 511. - qcom,ramp-high-index: Usage: required Value type: <u32> Definition: The high index of the LUT pattern where LPG ends up ramping to. Allowed range is 1 - 47. - qcom,ramp-low-index: Usage: required Value type: <u32> Definition: The low index of the LUT pattern from where LPG begins ramping from. Allowed range is 0 - 46. - qcom,ramp-from-low-to-high: Usage: optional Value type: <empty> Definition: The flag to specify the LPG ramping direction. The ramping direction is from low index to high index of the LUT pattern if it's specified. - qcom,ramp-pattern-repeat: Usage: optional Value type: <empty> Definition: The flag to specify if LPG would be ramping with the LUT pattern repeatedly. - qcom,ramp-toggle: Usage: optional Value type: <empty> Definition: The flag to specify if LPG would toggle the LUT pattern in ramping. If toggling enabled, LPG would return to the low index when high index is reached, or return to the high index when low index is reached. - qcom,ramp-pause-hi-count: Usage: optional Value type: <u32> Definition: The step count that LPG stop the output when it ramped up to the high index of the LUT. - qcom,ramp-pause-lo-count: Usage: optional Value type: <u32> Definition: The step count that LPG stop the output when it ramped up to the low index of the LUT. Example: pmi8998_lpg: lpg@b100 { compatible = "qcom,pwm-lpg"; reg = <0xb100 0x600>; reg-names = "lpg-base"; reg = <0xb100 0x600>, <0xb000 0x100>; reg-names = "lpg-base", "lut-base"; #pwm-cells = <2>; qcom,lut-patterns = <0 14 28 42 56 70 84 100 100 84 70 56 42 28 14 0>; lpg@3 { qcom,lpg-chan-id = <3>; qcom,ramp-step-ms = <200>; qcom,ramp-pause-hi-count = <10>; qcom,ramp-pause-lo-count = <10>; qcom,ramp-low-index = <0>; qcom,ramp-high-index = <15>; qcom,ramp-from-low-to-high; qcom,ramp-pattern-repeat; }; lpg@4 { qcom,lpg-chan-id = <4>; qcom,ramp-step-ms = <200>; qcom,ramp-pause-hi-count = <10>; qcom,ramp-pause-lo-count = <10>; qcom,ramp-low-index = <0>; qcom,ramp-high-index = <15>; qcom,ramp-from-low-to-high; qcom,ramp-pattern-repeat; }; lpg@5 { qcom,lpg-chan-id = <5>; qcom,ramp-step-ms = <200>; qcom,ramp-pause-hi-count = <10>; qcom,ramp-pause-lo-count = <10>; qcom,ramp-low-index = <0>; qcom,ramp-high-index = <15>; qcom,ramp-from-low-to-high; qcom,ramp-pattern-repeat; }; }; drivers/leds/leds-qti-tri-led.c +88 −7 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ struct led_setting { u32 off_ms; enum led_brightness brightness; bool blink; bool breath; }; struct qpnp_led_dev { Loading @@ -67,6 +68,7 @@ struct qpnp_led_dev { const char *default_trigger; u8 id; bool blinking; bool breathing; }; struct qpnp_tri_led_chip { Loading Loading @@ -122,6 +124,10 @@ static int __tri_led_config_pwm(struct qpnp_led_dev *led, pstate.enabled = !!(pwm->duty_ns != 0); pstate.period = pwm->period_ns; pstate.duty_cycle = pwm->duty_ns; pstate.output_type = led->led_setting.breath ? PWM_OUTPUT_MODULATED : PWM_OUTPUT_FIXED; /* Use default pattern in PWM device */ pstate.output_pattern = NULL; rc = pwm_apply_state(led->pwm_dev, &pstate); if (rc < 0) Loading Loading @@ -237,7 +243,9 @@ static int qpnp_tri_led_set(struct qpnp_led_dev *led) /* Use initial period if no blinking is required */ period_ns = led->pwm_setting.pre_period_ns; if (period_ns > INT_MAX / brightness) if (brightness == LED_OFF) duty_ns = 0; else if (period_ns > INT_MAX / brightness) duty_ns = (period_ns / LED_FULL) * brightness; else duty_ns = (period_ns * brightness) / LED_FULL; Loading @@ -261,9 +269,15 @@ static int qpnp_tri_led_set(struct qpnp_led_dev *led) if (led->led_setting.blink) { led->cdev.brightness = LED_FULL; led->blinking = true; led->breathing = false; } else if (led->led_setting.breath) { led->cdev.brightness = LED_FULL; led->blinking = false; led->breathing = true; } else { led->cdev.brightness = led->led_setting.brightness; led->blinking = false; led->breathing = false; } return rc; Loading @@ -281,7 +295,7 @@ static int qpnp_tri_led_set_brightness(struct led_classdev *led_cdev, brightness = LED_FULL; if (brightness == led->led_setting.brightness && !led->blinking) { !led->blinking && !led->breathing) { mutex_unlock(&led->lock); return 0; } Loading @@ -292,6 +306,7 @@ static int qpnp_tri_led_set_brightness(struct led_classdev *led_cdev, else led->led_setting.on_ms = 0; led->led_setting.blink = false; led->led_setting.breath = false; rc = qpnp_tri_led_set(led); if (rc) Loading Loading @@ -327,14 +342,17 @@ static int qpnp_tri_led_set_blink(struct led_classdev *led_cdev, if (*on_ms == 0) { led->led_setting.blink = false; led->led_setting.breath = false; led->led_setting.brightness = LED_OFF; } else if (*off_ms == 0) { led->led_setting.blink = false; led->led_setting.breath = false; led->led_setting.brightness = led->cdev.brightness; } else { led->led_setting.on_ms = *on_ms; led->led_setting.off_ms = *off_ms; led->led_setting.blink = true; led->led_setting.breath = false; } rc = qpnp_tri_led_set(led); Loading @@ -346,6 +364,52 @@ static int qpnp_tri_led_set_blink(struct led_classdev *led_cdev, return rc; } static ssize_t breath_show(struct device *dev, struct device_attribute *attr, char *buf) { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct qpnp_led_dev *led = container_of(led_cdev, struct qpnp_led_dev, cdev); return snprintf(buf, PAGE_SIZE, "%d\n", led->led_setting.breath); } static ssize_t breath_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int rc; bool breath; struct led_classdev *led_cdev = dev_get_drvdata(dev); struct qpnp_led_dev *led = container_of(led_cdev, struct qpnp_led_dev, cdev); rc = kstrtobool(buf, &breath); if (rc < 0) return rc; mutex_lock(&led->lock); if (led->breathing == breath) goto unlock; led->led_setting.blink = false; led->led_setting.breath = breath; led->led_setting.brightness = breath ? LED_FULL : LED_OFF; rc = qpnp_tri_led_set(led); if (rc < 0) dev_err(led->chip->dev, "Set led failed for %s, rc=%d\n", led->label, rc); unlock: mutex_unlock(&led->lock); return (rc < 0) ? rc : count; } static DEVICE_ATTR(breath, 0644, breath_show, breath_store); static const struct attribute *breath_attrs[] = { &dev_attr_breath.attr, NULL }; static int qpnp_tri_led_register(struct qpnp_tri_led_chip *chip) { struct qpnp_led_dev *led; Loading @@ -367,15 +431,30 @@ static int qpnp_tri_led_register(struct qpnp_tri_led_chip *chip) if (rc < 0) { dev_err(chip->dev, "%s led class device registering failed, rc=%d\n", led->label, rc); goto destroy; goto err_out; } if (pwm_get_output_type_supported(led->pwm_dev) & PWM_OUTPUT_MODULATED) { rc = sysfs_create_files(&led->cdev.dev->kobj, breath_attrs); if (rc < 0) { dev_err(chip->dev, "Create breath file for %s led failed, rc=%d\n", led->label, rc); goto err_out; } } } return 0; destroy: for (j = 0; j <= i; j++) mutex_destroy(&chip->leds[i].lock); err_out: for (j = 0; j <= i; j++) { if (j < i) sysfs_remove_files(&chip->leds[j].cdev.dev->kobj, breath_attrs); mutex_destroy(&chip->leds[j].lock); } return rc; } Loading Loading @@ -551,8 +630,10 @@ static int qpnp_tri_led_remove(struct platform_device *pdev) struct qpnp_tri_led_chip *chip = dev_get_drvdata(&pdev->dev); mutex_destroy(&chip->bus_lock); for (i = 0; i < chip->num_leds; i++) for (i = 0; i < chip->num_leds; i++) { sysfs_remove_files(&chip->leds[i].cdev.dev->kobj, breath_attrs); mutex_destroy(&chip->leds[i].lock); } dev_set_drvdata(chip->dev, NULL); return 0; } Loading drivers/pwm/pwm-qti-lpg.c +616 −30 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt +118 −7 Original line number Diff line number Diff line Loading @@ -11,26 +11,137 @@ device module in Qualcomm Technologies, Inc. PMIC chips. - reg: Usage: required Value type: <prop-encoded-array> Definition: Register base and length for LPG modules. The length varies based on the number of channels available in the PMIC chips. Definition: Register base and length for LPG and LUT modules. LPG size or length available per channel varies depending on the number of channels in PMIC. - reg-names: Usage: required Value type: <string> Definition: The name of the register defined in the reg property. It must be "lpg-base". It must have "lpg-base", "lut-base" is optional but it's required if any LPG channels support LUT mode. - #pwm-cells: Usage: required Value type: <u32> Definition: See Documentation/devicetree/bindings/pwm/pwm.txt; Definition: The number of cells in "pwms" property specified in PWM user nodes. It should be 2. The first cell is the PWM channel ID indexed from 0, and the second cell is the PWM default period in nanoseconds. - qcom,lut-patterns: Usage: optional Value type: <prop-encoded-array> Definition: Duty ratios in percentages for LPG working at LUT mode. These duty ratios will be translated into PWM values and stored in LUT module. The LUT module has resource to store 47 PWM values at max and shared for all LPG channels. This property is required if any LPG channels support LUT mode. Subnode is optional if LUT mode is not required, it's required if any LPG channels expected to be supported in LUT mode. Subnode properties: Subnodes for each LPG channel (lpg@X) can be defined if any of the following parameters needs to be configured for that channel. - qcom,lpg-chan-id: Usage: required Value type: <u32> Definition: The LPG channel's hardware ID indexed from 1. Allowed range is 1 - 8. Maximum value depends on the number of channels supported on PMIC. - qcom,ramp-step-ms: Usage: required Value type: <u32> Definition: The step duration in milliseconds for LPG staying at each duty specified in the LUT pattern. Allowed range is 1 - 511. - qcom,ramp-high-index: Usage: required Value type: <u32> Definition: The high index of the LUT pattern where LPG ends up ramping to. Allowed range is 1 - 47. - qcom,ramp-low-index: Usage: required Value type: <u32> Definition: The low index of the LUT pattern from where LPG begins ramping from. Allowed range is 0 - 46. - qcom,ramp-from-low-to-high: Usage: optional Value type: <empty> Definition: The flag to specify the LPG ramping direction. The ramping direction is from low index to high index of the LUT pattern if it's specified. - qcom,ramp-pattern-repeat: Usage: optional Value type: <empty> Definition: The flag to specify if LPG would be ramping with the LUT pattern repeatedly. - qcom,ramp-toggle: Usage: optional Value type: <empty> Definition: The flag to specify if LPG would toggle the LUT pattern in ramping. If toggling enabled, LPG would return to the low index when high index is reached, or return to the high index when low index is reached. - qcom,ramp-pause-hi-count: Usage: optional Value type: <u32> Definition: The step count that LPG stop the output when it ramped up to the high index of the LUT. - qcom,ramp-pause-lo-count: Usage: optional Value type: <u32> Definition: The step count that LPG stop the output when it ramped up to the low index of the LUT. Example: pmi8998_lpg: lpg@b100 { compatible = "qcom,pwm-lpg"; reg = <0xb100 0x600>; reg-names = "lpg-base"; reg = <0xb100 0x600>, <0xb000 0x100>; reg-names = "lpg-base", "lut-base"; #pwm-cells = <2>; qcom,lut-patterns = <0 14 28 42 56 70 84 100 100 84 70 56 42 28 14 0>; lpg@3 { qcom,lpg-chan-id = <3>; qcom,ramp-step-ms = <200>; qcom,ramp-pause-hi-count = <10>; qcom,ramp-pause-lo-count = <10>; qcom,ramp-low-index = <0>; qcom,ramp-high-index = <15>; qcom,ramp-from-low-to-high; qcom,ramp-pattern-repeat; }; lpg@4 { qcom,lpg-chan-id = <4>; qcom,ramp-step-ms = <200>; qcom,ramp-pause-hi-count = <10>; qcom,ramp-pause-lo-count = <10>; qcom,ramp-low-index = <0>; qcom,ramp-high-index = <15>; qcom,ramp-from-low-to-high; qcom,ramp-pattern-repeat; }; lpg@5 { qcom,lpg-chan-id = <5>; qcom,ramp-step-ms = <200>; qcom,ramp-pause-hi-count = <10>; qcom,ramp-pause-lo-count = <10>; qcom,ramp-low-index = <0>; qcom,ramp-high-index = <15>; qcom,ramp-from-low-to-high; qcom,ramp-pattern-repeat; }; };
drivers/leds/leds-qti-tri-led.c +88 −7 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ struct led_setting { u32 off_ms; enum led_brightness brightness; bool blink; bool breath; }; struct qpnp_led_dev { Loading @@ -67,6 +68,7 @@ struct qpnp_led_dev { const char *default_trigger; u8 id; bool blinking; bool breathing; }; struct qpnp_tri_led_chip { Loading Loading @@ -122,6 +124,10 @@ static int __tri_led_config_pwm(struct qpnp_led_dev *led, pstate.enabled = !!(pwm->duty_ns != 0); pstate.period = pwm->period_ns; pstate.duty_cycle = pwm->duty_ns; pstate.output_type = led->led_setting.breath ? PWM_OUTPUT_MODULATED : PWM_OUTPUT_FIXED; /* Use default pattern in PWM device */ pstate.output_pattern = NULL; rc = pwm_apply_state(led->pwm_dev, &pstate); if (rc < 0) Loading Loading @@ -237,7 +243,9 @@ static int qpnp_tri_led_set(struct qpnp_led_dev *led) /* Use initial period if no blinking is required */ period_ns = led->pwm_setting.pre_period_ns; if (period_ns > INT_MAX / brightness) if (brightness == LED_OFF) duty_ns = 0; else if (period_ns > INT_MAX / brightness) duty_ns = (period_ns / LED_FULL) * brightness; else duty_ns = (period_ns * brightness) / LED_FULL; Loading @@ -261,9 +269,15 @@ static int qpnp_tri_led_set(struct qpnp_led_dev *led) if (led->led_setting.blink) { led->cdev.brightness = LED_FULL; led->blinking = true; led->breathing = false; } else if (led->led_setting.breath) { led->cdev.brightness = LED_FULL; led->blinking = false; led->breathing = true; } else { led->cdev.brightness = led->led_setting.brightness; led->blinking = false; led->breathing = false; } return rc; Loading @@ -281,7 +295,7 @@ static int qpnp_tri_led_set_brightness(struct led_classdev *led_cdev, brightness = LED_FULL; if (brightness == led->led_setting.brightness && !led->blinking) { !led->blinking && !led->breathing) { mutex_unlock(&led->lock); return 0; } Loading @@ -292,6 +306,7 @@ static int qpnp_tri_led_set_brightness(struct led_classdev *led_cdev, else led->led_setting.on_ms = 0; led->led_setting.blink = false; led->led_setting.breath = false; rc = qpnp_tri_led_set(led); if (rc) Loading Loading @@ -327,14 +342,17 @@ static int qpnp_tri_led_set_blink(struct led_classdev *led_cdev, if (*on_ms == 0) { led->led_setting.blink = false; led->led_setting.breath = false; led->led_setting.brightness = LED_OFF; } else if (*off_ms == 0) { led->led_setting.blink = false; led->led_setting.breath = false; led->led_setting.brightness = led->cdev.brightness; } else { led->led_setting.on_ms = *on_ms; led->led_setting.off_ms = *off_ms; led->led_setting.blink = true; led->led_setting.breath = false; } rc = qpnp_tri_led_set(led); Loading @@ -346,6 +364,52 @@ static int qpnp_tri_led_set_blink(struct led_classdev *led_cdev, return rc; } static ssize_t breath_show(struct device *dev, struct device_attribute *attr, char *buf) { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct qpnp_led_dev *led = container_of(led_cdev, struct qpnp_led_dev, cdev); return snprintf(buf, PAGE_SIZE, "%d\n", led->led_setting.breath); } static ssize_t breath_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int rc; bool breath; struct led_classdev *led_cdev = dev_get_drvdata(dev); struct qpnp_led_dev *led = container_of(led_cdev, struct qpnp_led_dev, cdev); rc = kstrtobool(buf, &breath); if (rc < 0) return rc; mutex_lock(&led->lock); if (led->breathing == breath) goto unlock; led->led_setting.blink = false; led->led_setting.breath = breath; led->led_setting.brightness = breath ? LED_FULL : LED_OFF; rc = qpnp_tri_led_set(led); if (rc < 0) dev_err(led->chip->dev, "Set led failed for %s, rc=%d\n", led->label, rc); unlock: mutex_unlock(&led->lock); return (rc < 0) ? rc : count; } static DEVICE_ATTR(breath, 0644, breath_show, breath_store); static const struct attribute *breath_attrs[] = { &dev_attr_breath.attr, NULL }; static int qpnp_tri_led_register(struct qpnp_tri_led_chip *chip) { struct qpnp_led_dev *led; Loading @@ -367,15 +431,30 @@ static int qpnp_tri_led_register(struct qpnp_tri_led_chip *chip) if (rc < 0) { dev_err(chip->dev, "%s led class device registering failed, rc=%d\n", led->label, rc); goto destroy; goto err_out; } if (pwm_get_output_type_supported(led->pwm_dev) & PWM_OUTPUT_MODULATED) { rc = sysfs_create_files(&led->cdev.dev->kobj, breath_attrs); if (rc < 0) { dev_err(chip->dev, "Create breath file for %s led failed, rc=%d\n", led->label, rc); goto err_out; } } } return 0; destroy: for (j = 0; j <= i; j++) mutex_destroy(&chip->leds[i].lock); err_out: for (j = 0; j <= i; j++) { if (j < i) sysfs_remove_files(&chip->leds[j].cdev.dev->kobj, breath_attrs); mutex_destroy(&chip->leds[j].lock); } return rc; } Loading Loading @@ -551,8 +630,10 @@ static int qpnp_tri_led_remove(struct platform_device *pdev) struct qpnp_tri_led_chip *chip = dev_get_drvdata(&pdev->dev); mutex_destroy(&chip->bus_lock); for (i = 0; i < chip->num_leds; i++) for (i = 0; i < chip->num_leds; i++) { sysfs_remove_files(&chip->leds[i].cdev.dev->kobj, breath_attrs); mutex_destroy(&chip->leds[i].lock); } dev_set_drvdata(chip->dev, NULL); return 0; } Loading
drivers/pwm/pwm-qti-lpg.c +616 −30 File changed.Preview size limit exceeded, changes collapsed. Show changes