Loading Documentation/pwm.txt +28 −2 Original line number Original line Diff line number Diff line Loading @@ -42,9 +42,26 @@ variants of these functions, devm_pwm_get() and devm_pwm_put(), also exist. After being requested, a PWM has to be configured using: After being requested, a PWM has to be configured using: int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns); int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state); To start/stop toggling the PWM output use pwm_enable()/pwm_disable(). This API controls both the PWM period/duty_cycle config and the enable/disable state. The pwm_config(), pwm_enable() and pwm_disable() functions are just wrappers around pwm_apply_state() and should not be used if the user wants to change several parameter at once. For example, if you see pwm_config() and pwm_{enable,disable}() calls in the same function, this probably means you should switch to pwm_apply_state(). The PWM user API also allows one to query the PWM state with pwm_get_state(). In addition to the PWM state, the PWM API also exposes PWM arguments, which are the reference PWM config one should use on this PWM. PWM arguments are usually platform-specific and allows the PWM user to only care about dutycycle relatively to the full period (like, duty = 50% of the period). struct pwm_args contains 2 fields (period and polarity) and should be used to set the initial PWM config (usually done in the probe function of the PWM user). PWM arguments are retrieved with pwm_get_args(). Using PWMs with the sysfs interface Using PWMs with the sysfs interface ----------------------------------- ----------------------------------- Loading Loading @@ -105,6 +122,15 @@ goes low for the remainder of the period. Conversely, a signal with inversed polarity starts low for the duration of the duty cycle and goes high for the polarity starts low for the duration of the duty cycle and goes high for the remainder of the period. remainder of the period. Drivers are encouraged to implement ->apply() instead of the legacy ->enable(), ->disable() and ->config() methods. Doing that should provide atomicity in the PWM config workflow, which is required when the PWM controls a critical device (like a regulator). The implementation of ->get_state() (a method used to retrieve initial PWM state) is also encouraged for the same reason: letting the PWM user know about the current PWM state would allow him to avoid glitches. Locking Locking ------- ------- Loading arch/arm/mach-s3c24xx/mach-rx1950.c +6 −0 Original line number Original line Diff line number Diff line Loading @@ -496,6 +496,12 @@ static int rx1950_backlight_init(struct device *dev) return PTR_ERR(lcd_pwm); return PTR_ERR(lcd_pwm); } } /* * FIXME: pwm_apply_args() should be removed when switching to * the atomic PWM API. */ pwm_apply_args(lcd_pwm); rx1950_lcd_power(1); rx1950_lcd_power(1); rx1950_bl_power(1); rx1950_bl_power(1); Loading drivers/clk/clk-pwm.c +12 −5 Original line number Original line Diff line number Diff line Loading @@ -59,6 +59,7 @@ static int clk_pwm_probe(struct platform_device *pdev) struct clk_init_data init; struct clk_init_data init; struct clk_pwm *clk_pwm; struct clk_pwm *clk_pwm; struct pwm_device *pwm; struct pwm_device *pwm; struct pwm_args pargs; const char *clk_name; const char *clk_name; struct clk *clk; struct clk *clk; int ret; int ret; Loading @@ -71,22 +72,28 @@ static int clk_pwm_probe(struct platform_device *pdev) if (IS_ERR(pwm)) if (IS_ERR(pwm)) return PTR_ERR(pwm); return PTR_ERR(pwm); if (!pwm->period) { pwm_get_args(pwm, &pargs); if (!pargs.period) { dev_err(&pdev->dev, "invalid PWM period\n"); dev_err(&pdev->dev, "invalid PWM period\n"); return -EINVAL; return -EINVAL; } } if (of_property_read_u32(node, "clock-frequency", &clk_pwm->fixed_rate)) if (of_property_read_u32(node, "clock-frequency", &clk_pwm->fixed_rate)) clk_pwm->fixed_rate = NSEC_PER_SEC / pwm->period; clk_pwm->fixed_rate = NSEC_PER_SEC / pargs.period; if (pwm->period != NSEC_PER_SEC / clk_pwm->fixed_rate && if (pargs.period != NSEC_PER_SEC / clk_pwm->fixed_rate && pwm->period != DIV_ROUND_UP(NSEC_PER_SEC, clk_pwm->fixed_rate)) { pargs.period != DIV_ROUND_UP(NSEC_PER_SEC, clk_pwm->fixed_rate)) { dev_err(&pdev->dev, dev_err(&pdev->dev, "clock-frequency does not match PWM period\n"); "clock-frequency does not match PWM period\n"); return -EINVAL; return -EINVAL; } } ret = pwm_config(pwm, (pwm->period + 1) >> 1, pwm->period); /* * FIXME: pwm_apply_args() should be removed when switching to the * atomic PWM API. */ pwm_apply_args(pwm); ret = pwm_config(pwm, (pargs.period + 1) >> 1, pargs.period); if (ret < 0) if (ret < 0) return ret; return ret; Loading drivers/gpu/drm/i915/intel_panel.c +6 −0 Original line number Original line Diff line number Diff line Loading @@ -1640,6 +1640,12 @@ static int pwm_setup_backlight(struct intel_connector *connector, return -ENODEV; return -ENODEV; } } /* * FIXME: pwm_apply_args() should be removed when switching to * the atomic PWM API. */ pwm_apply_args(panel->backlight.pwm); retval = pwm_config(panel->backlight.pwm, CRC_PMIC_PWM_PERIOD_NS, retval = pwm_config(panel->backlight.pwm, CRC_PMIC_PWM_PERIOD_NS, CRC_PMIC_PWM_PERIOD_NS); CRC_PMIC_PWM_PERIOD_NS); if (retval < 0) { if (retval < 0) { Loading drivers/hwmon/pwm-fan.c +20 −6 Original line number Original line Diff line number Diff line Loading @@ -40,15 +40,18 @@ struct pwm_fan_ctx { static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) { { struct pwm_args pargs; unsigned long duty; unsigned long duty; int ret = 0; int ret = 0; pwm_get_args(ctx->pwm, &pargs); mutex_lock(&ctx->lock); mutex_lock(&ctx->lock); if (ctx->pwm_value == pwm) if (ctx->pwm_value == pwm) goto exit_set_pwm_err; goto exit_set_pwm_err; duty = DIV_ROUND_UP(pwm * (ctx->pwm->period - 1), MAX_PWM); duty = DIV_ROUND_UP(pwm * (pargs.period - 1), MAX_PWM); ret = pwm_config(ctx->pwm, duty, ctx->pwm->period); ret = pwm_config(ctx->pwm, duty, pargs.period); if (ret) if (ret) goto exit_set_pwm_err; goto exit_set_pwm_err; Loading Loading @@ -215,6 +218,7 @@ static int pwm_fan_probe(struct platform_device *pdev) { { struct thermal_cooling_device *cdev; struct thermal_cooling_device *cdev; struct pwm_fan_ctx *ctx; struct pwm_fan_ctx *ctx; struct pwm_args pargs; struct device *hwmon; struct device *hwmon; int duty_cycle; int duty_cycle; int ret; int ret; Loading @@ -233,11 +237,19 @@ static int pwm_fan_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ctx); platform_set_drvdata(pdev, ctx); /* * FIXME: pwm_apply_args() should be removed when switching to the * atomic PWM API. */ pwm_apply_args(ctx->pwm); /* Set duty cycle to maximum allowed */ /* Set duty cycle to maximum allowed */ duty_cycle = ctx->pwm->period - 1; pwm_get_args(ctx->pwm, &pargs); duty_cycle = pargs.period - 1; ctx->pwm_value = MAX_PWM; ctx->pwm_value = MAX_PWM; ret = pwm_config(ctx->pwm, duty_cycle, ctx->pwm->period); ret = pwm_config(ctx->pwm, duty_cycle, pargs.period); if (ret) { if (ret) { dev_err(&pdev->dev, "Failed to configure PWM\n"); dev_err(&pdev->dev, "Failed to configure PWM\n"); return ret; return ret; Loading Loading @@ -303,14 +315,16 @@ static int pwm_fan_suspend(struct device *dev) static int pwm_fan_resume(struct device *dev) static int pwm_fan_resume(struct device *dev) { { struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); struct pwm_args pargs; unsigned long duty; unsigned long duty; int ret; int ret; if (ctx->pwm_value == 0) if (ctx->pwm_value == 0) return 0; return 0; duty = DIV_ROUND_UP(ctx->pwm_value * (ctx->pwm->period - 1), MAX_PWM); pwm_get_args(ctx->pwm, &pargs); ret = pwm_config(ctx->pwm, duty, ctx->pwm->period); duty = DIV_ROUND_UP(ctx->pwm_value * (pargs.period - 1), MAX_PWM); ret = pwm_config(ctx->pwm, duty, pargs.period); if (ret) if (ret) return ret; return ret; return pwm_enable(ctx->pwm); return pwm_enable(ctx->pwm); Loading Loading
Documentation/pwm.txt +28 −2 Original line number Original line Diff line number Diff line Loading @@ -42,9 +42,26 @@ variants of these functions, devm_pwm_get() and devm_pwm_put(), also exist. After being requested, a PWM has to be configured using: After being requested, a PWM has to be configured using: int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns); int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state); To start/stop toggling the PWM output use pwm_enable()/pwm_disable(). This API controls both the PWM period/duty_cycle config and the enable/disable state. The pwm_config(), pwm_enable() and pwm_disable() functions are just wrappers around pwm_apply_state() and should not be used if the user wants to change several parameter at once. For example, if you see pwm_config() and pwm_{enable,disable}() calls in the same function, this probably means you should switch to pwm_apply_state(). The PWM user API also allows one to query the PWM state with pwm_get_state(). In addition to the PWM state, the PWM API also exposes PWM arguments, which are the reference PWM config one should use on this PWM. PWM arguments are usually platform-specific and allows the PWM user to only care about dutycycle relatively to the full period (like, duty = 50% of the period). struct pwm_args contains 2 fields (period and polarity) and should be used to set the initial PWM config (usually done in the probe function of the PWM user). PWM arguments are retrieved with pwm_get_args(). Using PWMs with the sysfs interface Using PWMs with the sysfs interface ----------------------------------- ----------------------------------- Loading Loading @@ -105,6 +122,15 @@ goes low for the remainder of the period. Conversely, a signal with inversed polarity starts low for the duration of the duty cycle and goes high for the polarity starts low for the duration of the duty cycle and goes high for the remainder of the period. remainder of the period. Drivers are encouraged to implement ->apply() instead of the legacy ->enable(), ->disable() and ->config() methods. Doing that should provide atomicity in the PWM config workflow, which is required when the PWM controls a critical device (like a regulator). The implementation of ->get_state() (a method used to retrieve initial PWM state) is also encouraged for the same reason: letting the PWM user know about the current PWM state would allow him to avoid glitches. Locking Locking ------- ------- Loading
arch/arm/mach-s3c24xx/mach-rx1950.c +6 −0 Original line number Original line Diff line number Diff line Loading @@ -496,6 +496,12 @@ static int rx1950_backlight_init(struct device *dev) return PTR_ERR(lcd_pwm); return PTR_ERR(lcd_pwm); } } /* * FIXME: pwm_apply_args() should be removed when switching to * the atomic PWM API. */ pwm_apply_args(lcd_pwm); rx1950_lcd_power(1); rx1950_lcd_power(1); rx1950_bl_power(1); rx1950_bl_power(1); Loading
drivers/clk/clk-pwm.c +12 −5 Original line number Original line Diff line number Diff line Loading @@ -59,6 +59,7 @@ static int clk_pwm_probe(struct platform_device *pdev) struct clk_init_data init; struct clk_init_data init; struct clk_pwm *clk_pwm; struct clk_pwm *clk_pwm; struct pwm_device *pwm; struct pwm_device *pwm; struct pwm_args pargs; const char *clk_name; const char *clk_name; struct clk *clk; struct clk *clk; int ret; int ret; Loading @@ -71,22 +72,28 @@ static int clk_pwm_probe(struct platform_device *pdev) if (IS_ERR(pwm)) if (IS_ERR(pwm)) return PTR_ERR(pwm); return PTR_ERR(pwm); if (!pwm->period) { pwm_get_args(pwm, &pargs); if (!pargs.period) { dev_err(&pdev->dev, "invalid PWM period\n"); dev_err(&pdev->dev, "invalid PWM period\n"); return -EINVAL; return -EINVAL; } } if (of_property_read_u32(node, "clock-frequency", &clk_pwm->fixed_rate)) if (of_property_read_u32(node, "clock-frequency", &clk_pwm->fixed_rate)) clk_pwm->fixed_rate = NSEC_PER_SEC / pwm->period; clk_pwm->fixed_rate = NSEC_PER_SEC / pargs.period; if (pwm->period != NSEC_PER_SEC / clk_pwm->fixed_rate && if (pargs.period != NSEC_PER_SEC / clk_pwm->fixed_rate && pwm->period != DIV_ROUND_UP(NSEC_PER_SEC, clk_pwm->fixed_rate)) { pargs.period != DIV_ROUND_UP(NSEC_PER_SEC, clk_pwm->fixed_rate)) { dev_err(&pdev->dev, dev_err(&pdev->dev, "clock-frequency does not match PWM period\n"); "clock-frequency does not match PWM period\n"); return -EINVAL; return -EINVAL; } } ret = pwm_config(pwm, (pwm->period + 1) >> 1, pwm->period); /* * FIXME: pwm_apply_args() should be removed when switching to the * atomic PWM API. */ pwm_apply_args(pwm); ret = pwm_config(pwm, (pargs.period + 1) >> 1, pargs.period); if (ret < 0) if (ret < 0) return ret; return ret; Loading
drivers/gpu/drm/i915/intel_panel.c +6 −0 Original line number Original line Diff line number Diff line Loading @@ -1640,6 +1640,12 @@ static int pwm_setup_backlight(struct intel_connector *connector, return -ENODEV; return -ENODEV; } } /* * FIXME: pwm_apply_args() should be removed when switching to * the atomic PWM API. */ pwm_apply_args(panel->backlight.pwm); retval = pwm_config(panel->backlight.pwm, CRC_PMIC_PWM_PERIOD_NS, retval = pwm_config(panel->backlight.pwm, CRC_PMIC_PWM_PERIOD_NS, CRC_PMIC_PWM_PERIOD_NS); CRC_PMIC_PWM_PERIOD_NS); if (retval < 0) { if (retval < 0) { Loading
drivers/hwmon/pwm-fan.c +20 −6 Original line number Original line Diff line number Diff line Loading @@ -40,15 +40,18 @@ struct pwm_fan_ctx { static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) { { struct pwm_args pargs; unsigned long duty; unsigned long duty; int ret = 0; int ret = 0; pwm_get_args(ctx->pwm, &pargs); mutex_lock(&ctx->lock); mutex_lock(&ctx->lock); if (ctx->pwm_value == pwm) if (ctx->pwm_value == pwm) goto exit_set_pwm_err; goto exit_set_pwm_err; duty = DIV_ROUND_UP(pwm * (ctx->pwm->period - 1), MAX_PWM); duty = DIV_ROUND_UP(pwm * (pargs.period - 1), MAX_PWM); ret = pwm_config(ctx->pwm, duty, ctx->pwm->period); ret = pwm_config(ctx->pwm, duty, pargs.period); if (ret) if (ret) goto exit_set_pwm_err; goto exit_set_pwm_err; Loading Loading @@ -215,6 +218,7 @@ static int pwm_fan_probe(struct platform_device *pdev) { { struct thermal_cooling_device *cdev; struct thermal_cooling_device *cdev; struct pwm_fan_ctx *ctx; struct pwm_fan_ctx *ctx; struct pwm_args pargs; struct device *hwmon; struct device *hwmon; int duty_cycle; int duty_cycle; int ret; int ret; Loading @@ -233,11 +237,19 @@ static int pwm_fan_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ctx); platform_set_drvdata(pdev, ctx); /* * FIXME: pwm_apply_args() should be removed when switching to the * atomic PWM API. */ pwm_apply_args(ctx->pwm); /* Set duty cycle to maximum allowed */ /* Set duty cycle to maximum allowed */ duty_cycle = ctx->pwm->period - 1; pwm_get_args(ctx->pwm, &pargs); duty_cycle = pargs.period - 1; ctx->pwm_value = MAX_PWM; ctx->pwm_value = MAX_PWM; ret = pwm_config(ctx->pwm, duty_cycle, ctx->pwm->period); ret = pwm_config(ctx->pwm, duty_cycle, pargs.period); if (ret) { if (ret) { dev_err(&pdev->dev, "Failed to configure PWM\n"); dev_err(&pdev->dev, "Failed to configure PWM\n"); return ret; return ret; Loading Loading @@ -303,14 +315,16 @@ static int pwm_fan_suspend(struct device *dev) static int pwm_fan_resume(struct device *dev) static int pwm_fan_resume(struct device *dev) { { struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); struct pwm_args pargs; unsigned long duty; unsigned long duty; int ret; int ret; if (ctx->pwm_value == 0) if (ctx->pwm_value == 0) return 0; return 0; duty = DIV_ROUND_UP(ctx->pwm_value * (ctx->pwm->period - 1), MAX_PWM); pwm_get_args(ctx->pwm, &pargs); ret = pwm_config(ctx->pwm, duty, ctx->pwm->period); duty = DIV_ROUND_UP(ctx->pwm_value * (pargs.period - 1), MAX_PWM); ret = pwm_config(ctx->pwm, duty, pargs.period); if (ret) if (ret) return ret; return ret; return pwm_enable(ctx->pwm); return pwm_enable(ctx->pwm); Loading