Skip to content

Commit

Permalink
Merge branch 'for-4.7/pwm-atomic' into for-next
Browse files Browse the repository at this point in the history
  • Loading branch information
thierryreding committed May 17, 2016
2 parents d2a3f20 + 23e3523 commit 18c5887
Show file tree
Hide file tree
Showing 22 changed files with 580 additions and 213 deletions.
30 changes: 28 additions & 2 deletions Documentation/pwm.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
-----------------------------------
Expand Down Expand Up @@ -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
-------

Expand Down
6 changes: 6 additions & 0 deletions arch/arm/mach-s3c24xx/mach-rx1950.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
17 changes: 12 additions & 5 deletions drivers/clk/clk-pwm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand Down
6 changes: 6 additions & 0 deletions drivers/gpu/drm/i915/intel_panel.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
26 changes: 20 additions & 6 deletions drivers/hwmon/pwm-fan.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
17 changes: 14 additions & 3 deletions drivers/input/misc/max77693-haptic.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,13 @@ struct max77693_haptic {

static int max77693_haptic_set_duty_cycle(struct max77693_haptic *haptic)
{
int delta = (haptic->pwm_dev->period + haptic->pwm_duty) / 2;
struct pwm_args pargs;
int delta;
int error;

error = pwm_config(haptic->pwm_dev, delta, haptic->pwm_dev->period);
pwm_get_args(haptic->pwm_dev, &pargs);
delta = (pargs.period + haptic->pwm_duty) / 2;
error = pwm_config(haptic->pwm_dev, delta, pargs.period);
if (error) {
dev_err(haptic->dev, "failed to configure pwm: %d\n", error);
return error;
Expand Down Expand Up @@ -234,6 +237,7 @@ static int max77693_haptic_play_effect(struct input_dev *dev, void *data,
struct ff_effect *effect)
{
struct max77693_haptic *haptic = input_get_drvdata(dev);
struct pwm_args pargs;
u64 period_mag_multi;

haptic->magnitude = effect->u.rumble.strong_magnitude;
Expand All @@ -245,7 +249,8 @@ static int max77693_haptic_play_effect(struct input_dev *dev, void *data,
* The formula to convert magnitude to pwm_duty as follows:
* - pwm_duty = (magnitude * pwm_period) / MAX_MAGNITUDE(0xFFFF)
*/
period_mag_multi = (u64)haptic->pwm_dev->period * haptic->magnitude;
pwm_get_args(haptic->pwm_dev, &pargs);
period_mag_multi = (u64)pargs.period * haptic->magnitude;
haptic->pwm_duty = (unsigned int)(period_mag_multi >>
MAX_MAGNITUDE_SHIFT);

Expand Down Expand Up @@ -329,6 +334,12 @@ static int max77693_haptic_probe(struct platform_device *pdev)
return PTR_ERR(haptic->pwm_dev);
}

/*
* FIXME: pwm_apply_args() should be removed when switching to the
* atomic PWM API.
*/
pwm_apply_args(haptic->pwm_dev);

haptic->motor_reg = devm_regulator_get(&pdev->dev, "haptic");
if (IS_ERR(haptic->motor_reg)) {
dev_err(&pdev->dev, "failed to get regulator\n");
Expand Down
6 changes: 6 additions & 0 deletions drivers/input/misc/max8997_haptic.c
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,12 @@ static int max8997_haptic_probe(struct platform_device *pdev)
error);
goto err_free_mem;
}

/*
* FIXME: pwm_apply_args() should be removed when switching to
* the atomic PWM API.
*/
pwm_apply_args(chip->pwm);
break;

default:
Expand Down
6 changes: 6 additions & 0 deletions drivers/input/misc/pwm-beeper.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ static int pwm_beeper_probe(struct platform_device *pdev)
goto err_free;
}

/*
* FIXME: pwm_apply_args() should be removed when switching to
* the atomic PWM API.
*/
pwm_apply_args(beeper->pwm);

beeper->input = input_allocate_device();
if (!beeper->input) {
dev_err(&pdev->dev, "Failed to allocate input device\n");
Expand Down
11 changes: 10 additions & 1 deletion drivers/leds/leds-pwm.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
struct led_pwm *led, struct device_node *child)
{
struct led_pwm_data *led_data = &priv->leds[priv->num_leds];
struct pwm_args pargs;
int ret;

led_data->active_low = led->active_low;
Expand All @@ -117,7 +118,15 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
else
led_data->cdev.brightness_set_blocking = led_pwm_set_blocking;

led_data->period = pwm_get_period(led_data->pwm);
/*
* FIXME: pwm_apply_args() should be removed when switching to the
* atomic PWM API.
*/
pwm_apply_args(led_data->pwm);

pwm_get_args(led_data->pwm, &pargs);

led_data->period = pargs.period;
if (!led_data->period && (led->pwm_period_ns > 0))
led_data->period = led->pwm_period_ns;

Expand Down
Loading

0 comments on commit 18c5887

Please sign in to comment.