Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 18c58878 authored by Thierry Reding's avatar Thierry Reding
Browse files

Merge branch 'for-4.7/pwm-atomic' into for-next

parents d2a3f206 23e3523f
Loading
Loading
Loading
Loading
+28 −2
Original line number Diff line number Diff line
@@ -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:

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
-----------------------------------
@@ -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
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
-------

+6 −0
Original line number Diff line number Diff line
@@ -496,6 +496,12 @@ static int rx1950_backlight_init(struct device *dev)
		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_bl_power(1);

+12 −5
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ static int clk_pwm_probe(struct platform_device *pdev)
	struct clk_init_data init;
	struct clk_pwm *clk_pwm;
	struct pwm_device *pwm;
	struct pwm_args pargs;
	const char *clk_name;
	struct clk *clk;
	int ret;
@@ -71,22 +72,28 @@ static int clk_pwm_probe(struct platform_device *pdev)
	if (IS_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");
		return -EINVAL;
	}

	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 &&
	    pwm->period != DIV_ROUND_UP(NSEC_PER_SEC, clk_pwm->fixed_rate)) {
	if (pargs.period != NSEC_PER_SEC / clk_pwm->fixed_rate &&
	    pargs.period != DIV_ROUND_UP(NSEC_PER_SEC, clk_pwm->fixed_rate)) {
		dev_err(&pdev->dev,
			"clock-frequency does not match PWM period\n");
		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)
		return ret;

+6 −0
Original line number Diff line number Diff line
@@ -1640,6 +1640,12 @@ static int pwm_setup_backlight(struct intel_connector *connector,
		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,
			    CRC_PMIC_PWM_PERIOD_NS);
	if (retval < 0) {
+20 −6
Original line number Diff line number Diff line
@@ -40,15 +40,18 @@ struct pwm_fan_ctx {

static int  __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
{
	struct pwm_args pargs;
	unsigned long duty;
	int ret = 0;

	pwm_get_args(ctx->pwm, &pargs);

	mutex_lock(&ctx->lock);
	if (ctx->pwm_value == pwm)
		goto exit_set_pwm_err;

	duty = DIV_ROUND_UP(pwm * (ctx->pwm->period - 1), MAX_PWM);
	ret = pwm_config(ctx->pwm, duty, ctx->pwm->period);
	duty = DIV_ROUND_UP(pwm * (pargs.period - 1), MAX_PWM);
	ret = pwm_config(ctx->pwm, duty, pargs.period);
	if (ret)
		goto exit_set_pwm_err;

@@ -215,6 +218,7 @@ static int pwm_fan_probe(struct platform_device *pdev)
{
	struct thermal_cooling_device *cdev;
	struct pwm_fan_ctx *ctx;
	struct pwm_args pargs;
	struct device *hwmon;
	int duty_cycle;
	int ret;
@@ -233,11 +237,19 @@ static int pwm_fan_probe(struct platform_device *pdev)

	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 */
	duty_cycle = ctx->pwm->period - 1;
	pwm_get_args(ctx->pwm, &pargs);

	duty_cycle = pargs.period - 1;
	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) {
		dev_err(&pdev->dev, "Failed to configure PWM\n");
		return ret;
@@ -303,14 +315,16 @@ static int pwm_fan_suspend(struct device *dev)
static int pwm_fan_resume(struct device *dev)
{
	struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
	struct pwm_args pargs;
	unsigned long duty;
	int ret;

	if (ctx->pwm_value == 0)
		return 0;

	duty = DIV_ROUND_UP(ctx->pwm_value * (ctx->pwm->period - 1), MAX_PWM);
	ret = pwm_config(ctx->pwm, duty, ctx->pwm->period);
	pwm_get_args(ctx->pwm, &pargs);
	duty = DIV_ROUND_UP(ctx->pwm_value * (pargs.period - 1), MAX_PWM);
	ret = pwm_config(ctx->pwm, duty, pargs.period);
	if (ret)
		return ret;
	return pwm_enable(ctx->pwm);
Loading