Skip to content

Commit

Permalink
Merge tag 'pwm/for-4.7-rc1' of git://git.kernel.org/pub/scm/linux/ker…
Browse files Browse the repository at this point in the history
…nel/git/thierry.reding/linux-pwm

Pull pwm updates from Thierry Reding:
 "This set of changes introduces an atomic API to the PWM subsystem.
  This is influenced by the DRM atomic API that was introduced a while
  back, though it is obviously a lot simpler.  The fundamental idea
  remains the same, though: drivers provide a single callback to
  implement the atomic configuration of a PWM channel.

  As a side-effect the PWM subsystem gains the ability for initial state
  retrieval, so that the logical state mirrors that of the hardware.
  Many use-cases don't care about this, but for others it is essential.

  These new features require changes in all users, which these patches
  take care of.  The core is transitioned to use the atomic callback if
  available and provides a fallback mechanism for other drivers.

  Changes to transition users and drivers to the atomic API are
  postponed to v4.8"

* tag 'pwm/for-4.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm: (30 commits)
  pwm: Add information about polarity, duty cycle and period to debugfs
  pwm: Switch to the atomic API
  pwm: Update documentation
  pwm: Add core infrastructure to allow atomic updates
  pwm: Add hardware readout infrastructure
  pwm: Move the enabled/disabled info into pwm_state
  pwm: Introduce the pwm_state concept
  pwm: Keep PWM state in sync with hardware state
  ARM: Explicitly apply PWM config extracted from pwm_args
  drm: i915: Explicitly apply PWM config extracted from pwm_args
  input: misc: pwm-beeper: Explicitly apply PWM config extracted from pwm_args
  input: misc: max8997: Explicitly apply PWM config extracted from pwm_args
  backlight: lm3630a: explicitly apply PWM config extracted from pwm_args
  backlight: lp855x: Explicitly apply PWM config extracted from pwm_args
  backlight: lp8788: Explicitly apply PWM config extracted from pwm_args
  backlight: pwm_bl: Use pwm_get_args() where appropriate
  fbdev: ssd1307fb: Use pwm_get_args() where appropriate
  regulator: pwm: Use pwm_get_args() where appropriate
  leds: pwm: Use pwm_get_args() where appropriate
  input: misc: max77693: Use pwm_get_args() where appropriate
  ...
  • Loading branch information
torvalds committed May 25, 2016
2 parents 1f93d2a + 18c5887 commit ecc5fbd
Show file tree
Hide file tree
Showing 22 changed files with 585 additions and 210 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 @@ -1638,6 +1638,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 @@ -306,6 +306,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 ecc5fbd

Please sign in to comment.