Commit ecc5fbd5 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'pwm/for-4.7-rc1' of...

Merge tag 'pwm/for-4.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/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
  ...
parents 1f93d2ab 18c58878
...@@ -42,9 +42,26 @@ variants of these functions, devm_pwm_get() and devm_pwm_put(), also exist. ...@@ -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
----------------------------------- -----------------------------------
...@@ -105,6 +122,15 @@ goes low for the remainder of the period. Conversely, a signal with inversed ...@@ -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
------- -------
......
...@@ -496,6 +496,12 @@ static int rx1950_backlight_init(struct device *dev) ...@@ -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);
......
...@@ -59,6 +59,7 @@ static int clk_pwm_probe(struct platform_device *pdev) ...@@ -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;
...@@ -71,22 +72,28 @@ static int clk_pwm_probe(struct platform_device *pdev) ...@@ -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;
......
...@@ -1638,6 +1638,12 @@ static int pwm_setup_backlight(struct intel_connector *connector, ...@@ -1638,6 +1638,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) {
......
...@@ -40,15 +40,18 @@ struct pwm_fan_ctx { ...@@ -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;
...@@ -215,6 +218,7 @@ static int pwm_fan_probe(struct platform_device *pdev) ...@@ -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;
...@@ -233,11 +237,19 @@ static int pwm_fan_probe(struct platform_device *pdev) ...@@ -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;
...@@ -303,14 +315,16 @@ static int pwm_fan_suspend(struct device *dev) ...@@ -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);
......
...@@ -70,10 +70,13 @@ struct max77693_haptic { ...@@ -70,10 +70,13 @@ struct max77693_haptic {
static int max77693_haptic_set_duty_cycle(struct max77693_haptic *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; 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) { if (error) {
dev_err(haptic->dev, "failed to configure pwm: %d\n", error); dev_err(haptic->dev, "failed to configure pwm: %d\n", error);
return error; return error;
...@@ -234,6 +237,7 @@ static int max77693_haptic_play_effect(struct input_dev *dev, void *data, ...@@ -234,6 +237,7 @@ static int max77693_haptic_play_effect(struct input_dev *dev, void *data,
struct ff_effect *effect) struct ff_effect *effect)
{ {
struct max77693_haptic *haptic = input_get_drvdata(dev); struct max77693_haptic *haptic = input_get_drvdata(dev);
struct pwm_args pargs;
u64 period_mag_multi; u64 period_mag_multi;
haptic->magnitude = effect->u.rumble.strong_magnitude; haptic->magnitude = effect->u.rumble.strong_magnitude;
...@@ -245,7 +249,8 @@ static int max77693_haptic_play_effect(struct input_dev *dev, void *data, ...@@ -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: * The formula to convert magnitude to pwm_duty as follows:
* - pwm_duty = (magnitude * pwm_period) / MAX_MAGNITUDE(0xFFFF) * - 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 >> haptic->pwm_duty = (unsigned int)(period_mag_multi >>
MAX_MAGNITUDE_SHIFT); MAX_MAGNITUDE_SHIFT);
...@@ -329,6 +334,12 @@ static int max77693_haptic_probe(struct platform_device *pdev) ...@@ -329,6 +334,12 @@ static int max77693_haptic_probe(struct platform_device *pdev)
return PTR_ERR(haptic->pwm_dev); 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"); haptic->motor_reg = devm_regulator_get(&pdev->dev, "haptic");
if (IS_ERR(haptic->motor_reg)) { if (IS_ERR(haptic->motor_reg)) {
dev_err(&pdev->dev, "failed to get regulator\n"); dev_err(&pdev->dev, "failed to get regulator\n");
......
...@@ -306,6 +306,12 @@ static int max8997_haptic_probe(struct platform_device *pdev) ...@@ -306,6 +306,12 @@ static int max8997_haptic_probe(struct platform_device *pdev)
error); error);
goto err_free_mem; goto err_free_mem;
} }
/*
* FIXME: pwm_apply_args() should be removed when switching to
* the atomic PWM API.
*/
pwm_apply_args(chip->pwm);
break; break;
default: default:
......
...@@ -87,6 +87,12 @@ static int pwm_beeper_probe(struct platform_device *pdev) ...@@ -87,6 +87,12 @@ static int pwm_beeper_probe(struct platform_device *pdev)
goto err_free; 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(); beeper->input = input_allocate_device();
if (!beeper->input) { if (!beeper->input) {
dev_err(&pdev->dev, "Failed to allocate input device\n"); dev_err(&pdev->dev, "Failed to allocate input device\n");
......
...@@ -91,6 +91,7 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv, ...@@ -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 *led, struct device_node *child)
{ {
struct led_pwm_data *led_data = &priv->leds[priv->num_leds]; struct led_pwm_data *led_data = &priv->leds[priv->num_leds];
struct pwm_args pargs;
int ret; int ret;
led_data->active_low = led->active_low; led_data->active_low = led->active_low;
...@@ -117,7 +118,15 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv, ...@@ -117,7 +118,15 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
else else
led_data->cdev.brightness_set_blocking = led_pwm_set_blocking; 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)) if (!led_data->period && (led->pwm_period_ns > 0))
led_data->period = led->pwm_period_ns; led_data->period = led->pwm_period_ns;
......
...@@ -75,6 +75,7 @@ static void free_pwms(struct pwm_chip *chip) ...@@ -75,6 +75,7 @@ static void free_pwms(struct pwm_chip *chip)
for (i = 0; i < chip->npwm; i++) { for (i = 0; i < chip->npwm; i++) {
struct pwm_device *pwm = &chip->pwms[i]; struct pwm_device *pwm = &chip->pwms[i];
radix_tree_delete(&pwm_tree, pwm->pwm); radix_tree_delete(&pwm_tree, pwm->pwm);
} }
...@@ -128,13 +129,6 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label) ...@@ -128,13 +129,6 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label)
set_bit(PWMF_REQUESTED, &pwm->flags); set_bit(PWMF_REQUESTED, &pwm->flags);
pwm->label = label; pwm->label = label;
/*
* FIXME: This should be removed once all PWM users properly make use
* of struct pwm_args to initialize the PWM device. As long as this is
* here, the PWM state and hardware state can get out of sync.
*/
pwm_apply_args(pwm);
return 0; return 0;
} }
...@@ -233,6 +227,19 @@ void *pwm_get_chip_data(struct pwm_device *pwm) ...@@ -233,6 +227,19 @@ void *pwm_get_chip_data(struct pwm_device *pwm)
} }
EXPORT_SYMBOL_GPL(pwm_get_chip_data); EXPORT_SYMBOL_GPL(pwm_get_chip_data);
static bool pwm_ops_check(const struct pwm_ops *ops)
{
/* driver supports legacy, non-atomic operation */
if (ops->config && ops->enable && ops->disable)
return true;
/* driver supports atomic operation */
if (ops->apply)
return true;
return false;
}
/** /**
* pwmchip_add_with_polarity() - register a new PWM chip * pwmchip_add_with_polarity() - register a new PWM chip
* @chip: the PWM chip to add * @chip: the PWM chip to add
...@@ -251,8 +258,10 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip, ...@@ -251,8 +258,10 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
unsigned int i; unsigned int i;
int ret; int ret;
if (!chip || !chip->dev || !chip->ops || !chip->ops->config || if (!chip || !chip->dev || !chip->ops || !chip->npwm)
!chip->ops->enable || !chip->ops->disable || !chip->npwm) return -EINVAL;
if (!pwm_ops_check(chip->ops))
return -EINVAL; return -EINVAL;
mutex_lock(&pwm_lock); mutex_lock(&pwm_lock);
...@@ -261,7 +270,7 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip, ...@@ -261,7 +270,7 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
if (ret < 0) if (ret < 0)
goto out; goto out;
chip->pwms = kzalloc(chip->npwm * sizeof(*pwm), GFP_KERNEL); chip->pwms = kcalloc(chip->npwm, sizeof(*pwm), GFP_KERNEL);
if (!chip->pwms) { if (!chip->pwms) {
ret = -ENOMEM; ret = -ENOMEM;
goto out; goto out;
...@@ -275,8 +284,10 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip, ...@@ -275,8 +284,10 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
pwm->chip = chip; pwm->chip = chip;
pwm->pwm = chip->base + i; pwm->pwm = chip->base + i;
pwm->hwpwm = i; pwm->hwpwm = i;
pwm->polarity = polarity; pwm->state.polarity = polarity;
mutex_init(&pwm->lock);
if (chip->ops->get_state)
chip->ops->get_state(chip, pwm, &pwm->state);
radix_tree_insert(&pwm_tree, pwm->pwm, pwm); radix_tree_insert(&pwm_tree, pwm->pwm, pwm);
} }
...@@ -436,107 +447,138 @@ void pwm_free(struct pwm_device *pwm) ...@@ -436,107 +447,138 @@ void pwm_free(struct pwm_device *pwm)
EXPORT_SYMBOL_GPL(pwm_free); EXPORT_SYMBOL_GPL(pwm_free);
/** /**
* pwm_config() - change a PWM device configuration * pwm_apply_state() - atomically apply a new state to a PWM device
* @pwm: PWM device * @pwm: PWM device
* @duty_ns: "on" time (in nanoseconds) * @state: new state to apply. This can be adjusted by the PWM driver
* @period_ns: duration (in nanoseconds) of one cycle * if the requested config is not achievable, for example,
* * ->duty_cycle and ->period might be approximated.
* Returns: 0 on success or a negative error code on failure.
*/ */
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)
{ {
int err; int err;
if (!pwm || duty_ns < 0 || period_ns <= 0 || duty_ns > period_ns) if (!pwm)
return -EINVAL; return -EINVAL;
err = pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns); if (!memcmp(state, &pwm->state, sizeof(*state)))
if (err) return 0;
return err;
pwm->duty_cycle = duty_ns;
pwm->period = period_ns;
return 0; if (pwm->chip->ops->apply) {
} err = pwm->chip->ops->apply(pwm->chip, pwm, state);
EXPORT_SYMBOL_GPL(pwm_config); if (err)
return err;
/** pwm->state = *state;
* pwm_set_polarity() - configure the polarity of a PWM signal } else {
* @pwm: PWM device /*
* @polarity: new polarity of the PWM signal * FIXME: restore the initial state in case of error.
* */
* Note that the polarity cannot be configured while the PWM device is if (state->polarity != pwm->state.polarity) {
* enabled. if (!pwm->chip->ops->set_polarity)
* return -ENOTSUPP;
* Returns: 0 on success or a negative error code on failure.
*/ /*
int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity) * Changing the polarity of a running PWM is
{ * only allowed when the PWM driver implements
int err; * ->apply().
*/
if (pwm->state.enabled) {
pwm->chip->ops->disable(pwm->chip, pwm);
pwm->state.enabled = false;
}
err = pwm->chip->ops->set_polarity(pwm->chip, pwm,
state->polarity);
if (err)
return err;
pwm->state.polarity = state->polarity;
}
if (!pwm || !pwm->chip->ops) if (state->period != pwm->state.period ||
return -EINVAL; state->duty_cycle != pwm->state.duty_cycle) {
err = pwm->chip->ops->config(pwm->chip, pwm,
state->duty_cycle,
state->period);
if (err)
return err;
if (!pwm->chip->ops->set_polarity) pwm->state.duty_cycle = state->duty_cycle;
return -ENOSYS; pwm->state.period = state->period;
}
mutex_lock(&pwm->lock); if (state->enabled != pwm->state.enabled) {
if (state->enabled) {
err = pwm->chip->ops->enable(pwm->chip, pwm);
if (err)
return err;
} else {
pwm->chip->ops->disable(pwm->chip, pwm);
}
if (pwm_is_enabled(pwm)) { pwm->state.enabled = state->enabled;
err = -EBUSY; }
goto unlock;
} }
err = pwm->chip->ops->set_polarity(pwm->chip, pwm, polarity); return 0;
if (err)
goto unlock;
pwm->polarity = polarity;
unlock:
mutex_unlock(&pwm->lock);
return err;
} }
EXPORT_SYMBOL_GPL(pwm_set_polarity); EXPORT_SYMBOL_GPL(pwm_apply_state);
/** /**
* pwm_enable() - start a PWM output toggling * pwm_adjust_config() - adjust the current PWM config to the PWM arguments
* @pwm: PWM device * @pwm: PWM device
* *
* Returns: 0 on success or a negative error code on failure. * This function will adjust the PWM config to the PWM arguments provided
* by the DT or PWM lookup table. This is particularly useful to adapt
* the bootloader config to the Linux one.
*/ */
int pwm_enable(struct pwm_device *pwm) int pwm_adjust_config(struct pwm_device *pwm)
{ {
int err = 0; struct pwm_state state;
struct pwm_args pargs;
if (!pwm) pwm_get_args(pwm, &pargs);
return -EINVAL; pwm_get_state(pwm, &state);
mutex_lock(&pwm->lock); /*
* If the current period is zero it means that either the PWM driver
* does not support initial state retrieval or the PWM has not yet
* been configured.
*
* In either case, we setup the new period and polarity, and assign a
* duty cycle of 0.
*/
if (!state.period) {
state.duty_cycle = 0;
state.period = pargs.period;
state.polarity = pargs.polarity;
if (!test_and_set_bit(PWMF_ENABLED, &pwm->flags)) { return pwm_apply_state(pwm, &state);
err = pwm->chip->ops->enable(pwm->chip, pwm);
if (err)
clear_bit(PWMF_ENABLED, &pwm->flags);
} }
mutex_unlock(&pwm->lock); /*
* Adjust the PWM duty cycle/period based on the period value provided
* in PWM args.
*/
if (pargs.period != state.period) {
u64 dutycycle = (u64)state.duty_cycle * pargs.period;
return err; do_div(dutycycle, state.period);
} state.duty_cycle = dutycycle;
EXPORT_SYMBOL_GPL(pwm_enable); state.period = pargs.period;
}
/** /*
* pwm_disable() - stop a PWM output toggling * If the polarity changed, we should also change the duty cycle.
* @pwm: PWM device */
*/ if (pargs.polarity != state.polarity) {
void pwm_disable(struct pwm_device *pwm) state.polarity = pargs.polarity;
{ state.duty_cycle = state.period - state.duty_cycle;
if (pwm && test_and_clear_bit(PWMF_ENABLED, &pwm->flags)) }
pwm->chip->ops->disable(pwm->chip, pwm);
return pwm_apply_state(pwm, &state);
} }
EXPORT_SYMBOL_GPL(pwm_disable); EXPORT_SYMBOL_GPL(pwm_adjust_config);
static struct pwm_chip *of_node_to_pwmchip(struct device_node *np) static struct pwm_chip *of_node_to_pwmchip(struct device_node *np)
{ {
...@@ -754,13 +796,13 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) ...@@ -754,13 +796,13 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
if (!chip) if (!chip)
goto out; goto out;
pwm->args.period = chosen->period;
pwm->args.polarity = chosen->polarity;
pwm = pwm_request_from_chip(chip, chosen->index, con_id ?: dev_id); pwm = pwm_request_from_chip(chip, chosen->index, con_id ?: dev_id);
if (IS_ERR(pwm)) if (IS_ERR(pwm))
goto out; goto out;
pwm->args.period = chosen->period;
pwm->args.polarity = chosen->polarity;
out: out:
mutex_unlock(&pwm_lookup_lock); mutex_unlock(&pwm_lookup_lock);
return pwm; return pwm;
...@@ -907,15 +949,23 @@ static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s) ...@@ -907,15 +949,23 @@ static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s)
for (i = 0; i < chip->npwm; i++) { for (i = 0; i < chip->npwm; i++) {
struct pwm_device *pwm = &chip->pwms[i]; struct pwm_device *pwm = &chip->pwms[i];
struct pwm_state state;
pwm_get_state(pwm, &state);
seq_printf(s, " pwm-%-3d (%-20.20s):", i, pwm->label); seq_printf(s, " pwm-%-3d (%-20.20s):", i, pwm->label);
if (test_bit(PWMF_REQUESTED, &pwm->flags)) if (test_bit(PWMF_REQUESTED, &pwm->flags))
seq_puts(s, " requested"); seq_puts(s, " requested");
if (pwm_is_enabled(pwm)) if (state.enabled)
seq_puts(s, " enabled"); seq_puts(s, " enabled");
seq_printf(s, " period: %u ns", state.period);
seq_printf(s, " duty: %u ns", state.duty_cycle);
seq_printf(s, " polarity: %s",
state.polarity ? "inverse" : "normal");
seq_puts(s, "\n"); seq_puts(s, "\n");
} }
} }
......
...@@ -75,7 +75,7 @@ static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm, ...@@ -75,7 +75,7 @@ static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm,
return -EINVAL; return -EINVAL;
} }
if (pwm->period != period_ns) { if (pwm_get_period(pwm) != period_ns) {
int clk_div; int clk_div;
/* changing the clk divisor, need to disable fisrt */ /* changing the clk divisor, need to disable fisrt */
......
...@@ -249,7 +249,7 @@ static int lpc18xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) ...@@ -249,7 +249,7 @@ static int lpc18xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
LPC18XX_PWM_EVSTATEMSK(lpc18xx_data->duty_event), LPC18XX_PWM_EVSTATEMSK(lpc18xx_data->duty_event),
LPC18XX_PWM_EVSTATEMSK_ALL); LPC18XX_PWM_EVSTATEMSK_ALL);
if (pwm->polarity == PWM_POLARITY_NORMAL) { if (pwm_get_polarity(pwm) == PWM_POLARITY_NORMAL) {
set_event = lpc18xx_pwm->period_event; set_event = lpc18xx_pwm->period_event;
clear_event = lpc18xx_data->duty_event; clear_event = lpc18xx_data->duty_event;
res_action = LPC18XX_PWM_RES_SET; res_action = LPC18XX_PWM_RES_SET;
......
...@@ -192,7 +192,7 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip, ...@@ -192,7 +192,7 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip,
load_value, load_value, match_value, match_value); load_value, load_value, match_value, match_value);
omap->pdata->set_pwm(omap->dm_timer, omap->pdata->set_pwm(omap->dm_timer,
pwm->polarity == PWM_POLARITY_INVERSED, pwm_get_polarity(pwm) == PWM_POLARITY_INVERSED,
true, true,
PWM_OMAP_DMTIMER_TRIGGER_OVERFLOW_AND_COMPARE); PWM_OMAP_DMTIMER_TRIGGER_OVERFLOW_AND_COMPARE);
......
...@@ -157,7 +157,7 @@ static int rcar_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -157,7 +157,7 @@ static int rcar_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
return div; return div;
/* Let the core driver set pwm->period if disabled and duty_ns == 0 */ /* Let the core driver set pwm->period if disabled and duty_ns == 0 */
if (!test_bit(PWMF_ENABLED, &pwm->flags) && !duty_ns) if (!pwm_is_enabled(pwm) && !duty_ns)
return 0; return 0;
rcar_pwm_update(rp, RCAR_PWMCR_SYNC, RCAR_PWMCR_SYNC, RCAR_PWMCR); rcar_pwm_update(rp, RCAR_PWMCR_SYNC, RCAR_PWMCR_SYNC, RCAR_PWMCR);
......
...@@ -354,7 +354,8 @@ static int sun4i_pwm_probe(struct platform_device *pdev) ...@@ -354,7 +354,8 @@ static int sun4i_pwm_probe(struct platform_device *pdev)
val = sun4i_pwm_readl(pwm, PWM_CTRL_REG); val = sun4i_pwm_readl(pwm, PWM_CTRL_REG);
for (i = 0; i < pwm->chip.npwm; i++) for (i = 0; i < pwm->chip.npwm; i++)
if (!(val & BIT_CH(PWM_ACT_STATE, i))) if (!(val & BIT_CH(PWM_ACT_STATE, i)))
pwm->chip.pwms[i].polarity = PWM_POLARITY_INVERSED; pwm_set_polarity(&pwm->chip.pwms[i],
PWM_POLARITY_INVERSED);
clk_disable_unprepare(pwm->clk); clk_disable_unprepare(pwm->clk);
return 0; return 0;
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
struct pwm_export { struct pwm_export {
struct device child; struct device child;
struct pwm_device *pwm; struct pwm_device *pwm;
struct mutex lock;
}; };
static struct pwm_export *child_to_pwm_export(struct device *child) static struct pwm_export *child_to_pwm_export(struct device *child)
...@@ -45,15 +46,20 @@ static ssize_t period_show(struct device *child, ...@@ -45,15 +46,20 @@ static ssize_t period_show(struct device *child,
char *buf) char *buf)
{ {
const struct pwm_device *pwm = child_to_pwm_device(child); const struct pwm_device *pwm = child_to_pwm_device(child);
struct pwm_state state;
return sprintf(buf, "%u\n", pwm_get_period(pwm)); pwm_get_state(pwm, &state);
return sprintf(buf, "%u\n", state.period);
} }
static ssize_t period_store(struct device *child, static ssize_t period_store(struct device *child,
struct device_attribute *attr, struct device_attribute *attr,
const char *buf, size_t size) const char *buf, size_t size)
{ {
struct pwm_device *pwm = child_to_pwm_device(child); struct pwm_export *export = child_to_pwm_export(child);
struct pwm_device *pwm = export->pwm;
struct pwm_state state;
unsigned int val; unsigned int val;
int ret; int ret;
...@@ -61,7 +67,11 @@ static ssize_t period_store(struct device *child, ...@@ -61,7 +67,11 @@ static ssize_t period_store(struct device *child,
if (ret) if (ret)
return ret; return ret;
ret = pwm_config(pwm, pwm_get_duty_cycle(pwm), val); mutex_lock(&export->lock);
pwm_get_state(pwm, &state);
state.period = val;
ret = pwm_apply_state(pwm, &state);
mutex_unlock(&export->lock);
return ret ? : size; return ret ? : size;
} }
...@@ -71,15 +81,20 @@ static ssize_t duty_cycle_show(struct device *child, ...@@ -71,15 +81,20 @@ static ssize_t duty_cycle_show(struct device *child,
char *buf) char *buf)
{ {
const struct pwm_device *pwm = child_to_pwm_device(child); const struct pwm_device *pwm = child_to_pwm_device(child);
struct pwm_state state;
pwm_get_state(pwm, &state);
return sprintf(buf, "%u\n", pwm_get_duty_cycle(pwm)); return sprintf(buf, "%u\n", state.duty_cycle);
} }
static ssize_t duty_cycle_store(struct device *child, static ssize_t duty_cycle_store(struct device *child,
struct device_attribute *attr, struct device_attribute *attr,
const char *buf, size_t size) const char *buf, size_t size)
{ {
struct pwm_device *pwm = child_to_pwm_device(child); struct pwm_export *export = child_to_pwm_export(child);
struct pwm_device *pwm = export->pwm;
struct pwm_state state;
unsigned int val; unsigned int val;
int ret; int ret;
...@@ -87,7 +102,11 @@ static ssize_t duty_cycle_store(struct device *child, ...@@ -87,7 +102,11 @@ static ssize_t duty_cycle_store(struct device *child,
if (ret) if (ret)
return ret; return ret;
ret = pwm_config(pwm, val, pwm_get_period(pwm)); mutex_lock(&export->lock);
pwm_get_state(pwm, &state);
state.duty_cycle = val;
ret = pwm_apply_state(pwm, &state);
mutex_unlock(&export->lock);
return ret ? : size; return ret ? : size;
} }
...@@ -97,33 +116,46 @@ static ssize_t enable_show(struct device *child, ...@@ -97,33 +116,46 @@ static ssize_t enable_show(struct device *child,
char *buf) char *buf)
{ {
const struct pwm_device *pwm = child_to_pwm_device(child); const struct pwm_device *pwm = child_to_pwm_device(child);
struct pwm_state state;
pwm_get_state(pwm, &state);
return sprintf(buf, "%d\n", pwm_is_enabled(pwm)); return sprintf(buf, "%d\n", state.enabled);
} }
static ssize_t enable_store(struct device *child, static ssize_t enable_store(struct device *child,
struct device_attribute *attr, struct device_attribute *attr,
const char *buf, size_t size) const char *buf, size_t size)
{ {
struct pwm_device *pwm = child_to_pwm_device(child); struct pwm_export *export = child_to_pwm_export(child);
struct pwm_device *pwm = export->pwm;
struct pwm_state state;
int val, ret; int val, ret;
ret = kstrtoint(buf, 0, &val); ret = kstrtoint(buf, 0, &val);
if (ret) if (ret)
return ret; return ret;
mutex_lock(&export->lock);
pwm_get_state(pwm, &state);
switch (val) { switch (val) {
case 0: case 0:
pwm_disable(pwm); state.enabled = false;
break; break;
case 1: case 1:
ret = pwm_enable(pwm); state.enabled = true;
break; break;
default: default:
ret = -EINVAL; ret = -EINVAL;
break; goto unlock;
} }
pwm_apply_state(pwm, &state);
unlock:
mutex_unlock(&export->lock);
return ret ? : size; return ret ? : size;
} }
...@@ -133,8 +165,11 @@ static ssize_t polarity_show(struct device *child, ...@@ -133,8 +165,11 @@ static ssize_t polarity_show(struct device *child,
{ {
const struct pwm_device *pwm = child_to_pwm_device(child); const struct pwm_device *pwm = child_to_pwm_device(child);
const char *polarity = "unknown"; const char *polarity = "unknown";
struct pwm_state state;
pwm_get_state(pwm, &state);
switch (pwm_get_polarity(pwm)) { switch (state.polarity) {
case PWM_POLARITY_NORMAL: case PWM_POLARITY_NORMAL:
polarity = "normal"; polarity = "normal";
break; break;
...@@ -151,8 +186,10 @@ static ssize_t polarity_store(struct device *child, ...@@ -151,8 +186,10 @@ static ssize_t polarity_store(struct device *child,
struct device_attribute *attr, struct device_attribute *attr,
const char *buf, size_t size) const char *buf, size_t size)
{ {
struct pwm_device *pwm = child_to_pwm_device(child); struct pwm_export *export = child_to_pwm_export(child);
struct pwm_device *pwm = export->pwm;
enum pwm_polarity polarity; enum pwm_polarity polarity;
struct pwm_state state;
int ret; int ret;
if (sysfs_streq(buf, "normal")) if (sysfs_streq(buf, "normal"))
...@@ -162,7 +199,11 @@ static ssize_t polarity_store(struct device *child, ...@@ -162,7 +199,11 @@ static ssize_t polarity_store(struct device *child,
else else
return -EINVAL; return -EINVAL;
ret = pwm_set_polarity(pwm, polarity); mutex_lock(&export->lock);
pwm_get_state(pwm, &state);
state.polarity = polarity;
ret = pwm_apply_state(pwm, &state);
mutex_unlock(&export->lock);
return ret ? : size; return ret ? : size;
} }
...@@ -203,6 +244,7 @@ static int pwm_export_child(struct device *parent, struct pwm_device *pwm) ...@@ -203,6 +244,7 @@ static int pwm_export_child(struct device *parent, struct pwm_device *pwm)
} }
export->pwm = pwm; export->pwm = pwm;
mutex_init(&export->lock);
export->child.release = pwm_export_release; export->child.release = pwm_export_release;
export->child.parent = parent; export->child.parent = parent;
......
...@@ -162,7 +162,7 @@ static int lm3630a_intr_config(struct lm3630a_chip *pchip) ...@@ -162,7 +162,7 @@ static int lm3630a_intr_config(struct lm3630a_chip *pchip)
static void lm3630a_pwm_ctrl(struct lm3630a_chip *pchip, int br, int br_max) static void lm3630a_pwm_ctrl(struct lm3630a_chip *pchip, int br, int br_max)
{ {
unsigned int period = pwm_get_period(pchip->pwmd); unsigned int period = pchip->pdata->pwm_period;
unsigned int duty = br * period / br_max; unsigned int duty = br * period / br_max;
pwm_config(pchip->pwmd, duty, period); pwm_config(pchip->pwmd, duty, period);
...@@ -424,8 +424,13 @@ static int lm3630a_probe(struct i2c_client *client, ...@@ -424,8 +424,13 @@ static int lm3630a_probe(struct i2c_client *client,
dev_err(&client->dev, "fail : get pwm device\n"); dev_err(&client->dev, "fail : get pwm device\n");
return PTR_ERR(pchip->pwmd); return PTR_ERR(pchip->pwmd);
} }
/*
* FIXME: pwm_apply_args() should be removed when switching to
* the atomic PWM API.
*/
pwm_apply_args(pchip->pwmd);
} }
pchip->pwmd->period = pdata->pwm_period;
/* interrupt enable : irq 0 is not allowed */ /* interrupt enable : irq 0 is not allowed */
pchip->irq = client->irq; pchip->irq = client->irq;
......
...@@ -246,6 +246,12 @@ static void lp855x_pwm_ctrl(struct lp855x *lp, int br, int max_br) ...@@ -246,6 +246,12 @@ static void lp855x_pwm_ctrl(struct lp855x *lp, int br, int max_br)
return; return;
lp->pwm = pwm; lp->pwm = pwm;
/*
* FIXME: pwm_apply_args() should be removed when switching to
* the atomic PWM API.
*/
pwm_apply_args(pwm);
} }
pwm_config(lp->pwm, duty, period); pwm_config(lp->pwm, duty, period);
......
...@@ -145,6 +145,12 @@ static void lp8788_pwm_ctrl(struct lp8788_bl *bl, int br, int max_br) ...@@ -145,6 +145,12 @@ static void lp8788_pwm_ctrl(struct lp8788_bl *bl, int br, int max_br)
} }
bl->pwm = pwm; bl->pwm = pwm;
/*
* FIXME: pwm_apply_args() should be removed when switching to
* the atomic PWM API.
*/
pwm_apply_args(pwm);
} }
pwm_config(bl->pwm, duty, period); pwm_config(bl->pwm, duty, period);
......
...@@ -201,6 +201,7 @@ static int pwm_backlight_probe(struct platform_device *pdev) ...@@ -201,6 +201,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
struct device_node *node = pdev->dev.of_node; struct device_node *node = pdev->dev.of_node;
struct pwm_bl_data *pb; struct pwm_bl_data *pb;
int initial_blank = FB_BLANK_UNBLANK; int initial_blank = FB_BLANK_UNBLANK;
struct pwm_args pargs;
int ret; int ret;
if (!data) { if (!data) {
...@@ -306,17 +307,22 @@ static int pwm_backlight_probe(struct platform_device *pdev) ...@@ -306,17 +307,22 @@ static int pwm_backlight_probe(struct platform_device *pdev)
dev_dbg(&pdev->dev, "got pwm for backlight\n"); dev_dbg(&pdev->dev, "got pwm for backlight\n");
/*
* FIXME: pwm_apply_args() should be removed when switching to
* the atomic PWM API.
*/
pwm_apply_args(pb->pwm);
/* /*
* The DT case will set the pwm_period_ns field to 0 and store the * The DT case will set the pwm_period_ns field to 0 and store the
* period, parsed from the DT, in the PWM device. For the non-DT case, * period, parsed from the DT, in the PWM device. For the non-DT case,
* set the period from platform data if it has not already been set * set the period from platform data if it has not already been set
* via the PWM lookup table. * via the PWM lookup table.
*/ */
pb->period = pwm_get_period(pb->pwm); pwm_get_args(pb->pwm, &pargs);
if (!pb->period && (data->pwm_period_ns > 0)) { pb->period = pargs.period;
if (!pb->period && (data->pwm_period_ns > 0))
pb->period = data->pwm_period_ns; pb->period = data->pwm_period_ns;
pwm_set_period(pb->pwm, data->pwm_period_ns);
}
pb->lth_brightness = data->lth_brightness * (pb->period / pb->scale); pb->lth_brightness = data->lth_brightness * (pb->period / pb->scale);
......
...@@ -286,6 +286,7 @@ static int ssd1307fb_init(struct ssd1307fb_par *par) ...@@ -286,6 +286,7 @@ static int ssd1307fb_init(struct ssd1307fb_par *par)
{ {
int ret; int ret;
u32 precharge, dclk, com_invdir, compins; u32 precharge, dclk, com_invdir, compins;
struct pwm_args pargs;
if (par->device_info->need_pwm) { if (par->device_info->need_pwm) {
par->pwm = pwm_get(&par->client->dev, NULL); par->pwm = pwm_get(&par->client->dev, NULL);
...@@ -294,7 +295,15 @@ static int ssd1307fb_init(struct ssd1307fb_par *par) ...@@ -294,7 +295,15 @@ static int ssd1307fb_init(struct ssd1307fb_par *par)
return PTR_ERR(par->pwm); return PTR_ERR(par->pwm);
} }
par->pwm_period = pwm_get_period(par->pwm); /*
* FIXME: pwm_apply_args() should be removed when switching to
* the atomic PWM API.
*/
pwm_apply_args(par->pwm);
pwm_get_args(par->pwm, &pargs);
par->pwm_period = pargs.period;
/* Enable the PWM */ /* Enable the PWM */
pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period); pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
pwm_enable(par->pwm); pwm_enable(par->pwm);
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment