Merge tag 'pwm/for-4.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 25 May 2016 17:40:15 +0000 (10:40 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 25 May 2016 17:40:15 +0000 (10:40 -0700)
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
  ...

22 files changed:
Documentation/pwm.txt
arch/arm/mach-s3c24xx/mach-rx1950.c
drivers/clk/clk-pwm.c
drivers/gpu/drm/i915/intel_panel.c
drivers/hwmon/pwm-fan.c
drivers/input/misc/max77693-haptic.c
drivers/input/misc/max8997_haptic.c
drivers/input/misc/pwm-beeper.c
drivers/leds/leds-pwm.c
drivers/pwm/core.c
drivers/pwm/pwm-crc.c
drivers/pwm/pwm-lpc18xx-sct.c
drivers/pwm/pwm-omap-dmtimer.c
drivers/pwm/pwm-rcar.c
drivers/pwm/pwm-sun4i.c
drivers/pwm/sysfs.c
drivers/video/backlight/lm3630a_bl.c
drivers/video/backlight/lp855x_bl.c
drivers/video/backlight/lp8788_bl.c
drivers/video/backlight/pwm_bl.c
drivers/video/fbdev/ssd1307fb.c
include/linux/pwm.h

index ca895fd211e4e9f5f6bd0fc6a13bf60d9a0c14b2..789b27c6ec996735932131315ee7a5b9aebd0c7d 100644 (file)
@@ -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
 -------
 
index 774c982a7b7ed4260a49ca01d7d9714b188f65d4..25a139bb9826dadfafd327bb1f2e1e4163993566 100644 (file)
@@ -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);
 
index 883045814dac2536fcb667b36e3db01d8715596e..1630a1f085f70cfeb7983b1329da1166ff6eb8e2 100644 (file)
@@ -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;
 
index a0788763757b0d650ab25d213aea9d1b5c8c032d..8357d571553a56471ff42ab0f048bab7f52f475f 100644 (file)
@@ -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) {
index 3e23003f78b01ca731e8c232f38e1132eccf251f..f9af3935b427c4cef758197fd4e11b4b7759c2eb 100644 (file)
@@ -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);
index 6d96bff32a0e3776190ee2c34bf2ea461866c057..29ddeb7be84bb485f676ea1fb632db48a53d9cfc 100644 (file)
@@ -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;
@@ -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;
@@ -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);
 
@@ -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");
index 8d6326d7e7beaf1875bb95af385dff1285f10b0e..99bc762881d5d40bc0c0788a59698f227da8737d 100644 (file)
@@ -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:
index f2261ab5470126f0935c3ca9d3830b55699dc378..8d71332687455d51d0391d2f5425d3cbf48edd8f 100644 (file)
@@ -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");
index 4783bacb2e9d8e48c043223fd89643069d93f4b9..a9145aa7f36a017327f9c7b74140ec5d1a730e40 100644 (file)
@@ -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;
@@ -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;
 
index 680fbc795a0a350a9c55f5e10e10a7090a8bfaea..dba3843c53b8d2162d10100d2473be79014d4b4b 100644 (file)
@@ -75,6 +75,7 @@ static void free_pwms(struct pwm_chip *chip)
 
        for (i = 0; i < chip->npwm; i++) {
                struct pwm_device *pwm = &chip->pwms[i];
+
                radix_tree_delete(&pwm_tree, pwm->pwm);
        }
 
@@ -128,13 +129,6 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label)
        set_bit(PWMF_REQUESTED, &pwm->flags);
        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;
 }
 
@@ -233,6 +227,19 @@ void *pwm_get_chip_data(struct pwm_device *pwm)
 }
 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
  * @chip: the PWM chip to add
@@ -251,8 +258,10 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
        unsigned int i;
        int ret;
 
-       if (!chip || !chip->dev || !chip->ops || !chip->ops->config ||
-           !chip->ops->enable || !chip->ops->disable || !chip->npwm)
+       if (!chip || !chip->dev || !chip->ops || !chip->npwm)
+               return -EINVAL;
+
+       if (!pwm_ops_check(chip->ops))
                return -EINVAL;
 
        mutex_lock(&pwm_lock);
@@ -261,7 +270,7 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
        if (ret < 0)
                goto out;
 
-       chip->pwms = kzalloc(chip->npwm * sizeof(*pwm), GFP_KERNEL);
+       chip->pwms = kcalloc(chip->npwm, sizeof(*pwm), GFP_KERNEL);
        if (!chip->pwms) {
                ret = -ENOMEM;
                goto out;
@@ -275,8 +284,10 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
                pwm->chip = chip;
                pwm->pwm = chip->base + i;
                pwm->hwpwm = i;
-               pwm->polarity = polarity;
-               mutex_init(&pwm->lock);
+               pwm->state.polarity = polarity;
+
+               if (chip->ops->get_state)
+                       chip->ops->get_state(chip, pwm, &pwm->state);
 
                radix_tree_insert(&pwm_tree, pwm->pwm, pwm);
        }
@@ -436,107 +447,138 @@ void pwm_free(struct pwm_device *pwm)
 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
- * @duty_ns: "on" time (in nanoseconds)
- * @period_ns: duration (in nanoseconds) of one cycle
- *
- * Returns: 0 on success or a negative error code on failure.
+ * @state: new state to apply. This can be adjusted by the PWM driver
+ *        if the requested config is not achievable, for example,
+ *        ->duty_cycle and ->period might be approximated.
  */
-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;
 
-       if (!pwm || duty_ns < 0 || period_ns <= 0 || duty_ns > period_ns)
+       if (!pwm)
                return -EINVAL;
 
-       err = pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns);
-       if (err)
-               return err;
-
-       pwm->duty_cycle = duty_ns;
-       pwm->period = period_ns;
+       if (!memcmp(state, &pwm->state, sizeof(*state)))
+               return 0;
 
-       return 0;
-}
-EXPORT_SYMBOL_GPL(pwm_config);
+       if (pwm->chip->ops->apply) {
+               err = pwm->chip->ops->apply(pwm->chip, pwm, state);
+               if (err)
+                       return err;
 
-/**
- * pwm_set_polarity() - configure the polarity of a PWM signal
- * @pwm: PWM device
- * @polarity: new polarity of the PWM signal
- *
- * Note that the polarity cannot be configured while the PWM device is
- * enabled.
- *
- * Returns: 0 on success or a negative error code on failure.
- */
-int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity)
-{
-       int err;
+               pwm->state = *state;
+       } else {
+               /*
+                * FIXME: restore the initial state in case of error.
+                */
+               if (state->polarity != pwm->state.polarity) {
+                       if (!pwm->chip->ops->set_polarity)
+                               return -ENOTSUPP;
+
+                       /*
+                        * Changing the polarity of a running PWM is
+                        * only allowed when the PWM driver implements
+                        * ->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)
-               return -EINVAL;
+               if (state->period != pwm->state.period ||
+                   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)
-               return -ENOSYS;
+                       pwm->state.duty_cycle = state->duty_cycle;
+                       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)) {
-               err = -EBUSY;
-               goto unlock;
+                       pwm->state.enabled = state->enabled;
+               }
        }
 
-       err = pwm->chip->ops->set_polarity(pwm->chip, pwm, polarity);
-       if (err)
-               goto unlock;
-
-       pwm->polarity = polarity;
-
-unlock:
-       mutex_unlock(&pwm->lock);
-       return err;
+       return 0;
 }
-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
  *
- * 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)
-               return -EINVAL;
+       pwm_get_args(pwm, &pargs);
+       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)) {
-               err = pwm->chip->ops->enable(pwm->chip, pwm);
-               if (err)
-                       clear_bit(PWMF_ENABLED, &pwm->flags);
+               return pwm_apply_state(pwm, &state);
        }
 
-       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;
-}
-EXPORT_SYMBOL_GPL(pwm_enable);
+               do_div(dutycycle, state.period);
+               state.duty_cycle = dutycycle;
+               state.period = pargs.period;
+       }
 
-/**
- * pwm_disable() - stop a PWM output toggling
- * @pwm: PWM device
- */
-void pwm_disable(struct pwm_device *pwm)
-{
-       if (pwm && test_and_clear_bit(PWMF_ENABLED, &pwm->flags))
-               pwm->chip->ops->disable(pwm->chip, pwm);
+       /*
+        * If the polarity changed, we should also change the duty cycle.
+        */
+       if (pargs.polarity != state.polarity) {
+               state.polarity = pargs.polarity;
+               state.duty_cycle = state.period - state.duty_cycle;
+       }
+
+       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)
 {
@@ -754,13 +796,13 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
        if (!chip)
                goto out;
 
-       pwm->args.period = chosen->period;
-       pwm->args.polarity = chosen->polarity;
-
        pwm = pwm_request_from_chip(chip, chosen->index, con_id ?: dev_id);
        if (IS_ERR(pwm))
                goto out;
 
+       pwm->args.period = chosen->period;
+       pwm->args.polarity = chosen->polarity;
+
 out:
        mutex_unlock(&pwm_lookup_lock);
        return pwm;
@@ -907,15 +949,23 @@ static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s)
 
        for (i = 0; i < chip->npwm; 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);
 
                if (test_bit(PWMF_REQUESTED, &pwm->flags))
                        seq_puts(s, " requested");
 
-               if (pwm_is_enabled(pwm))
+               if (state.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");
        }
 }
index 7101c7020bf454700f06ed525adf5a6d8757298e..bd0ebd04856a8a2988162d10cbd5e0204eb4639d 100644 (file)
@@ -75,7 +75,7 @@ static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm,
                return -EINVAL;
        }
 
-       if (pwm->period != period_ns) {
+       if (pwm_get_period(pwm) != period_ns) {
                int clk_div;
 
                /* changing the clk divisor, need to disable fisrt */
index 9861fed4e67d04e8d9f8f8488c7c276b3560fe7d..19dc64cab2f0f9c88b6aa15275a63b936bdf34c8 100644 (file)
@@ -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_ALL);
 
-       if (pwm->polarity == PWM_POLARITY_NORMAL) {
+       if (pwm_get_polarity(pwm) == PWM_POLARITY_NORMAL) {
                set_event = lpc18xx_pwm->period_event;
                clear_event = lpc18xx_data->duty_event;
                res_action = LPC18XX_PWM_RES_SET;
index b7e6ecba7d5c23422323acb4dfa6c6fe432a3a29..3e95090cd7cf5c21c758225b547cb5c5c57ed7e0 100644 (file)
@@ -192,7 +192,7 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip,
                load_value, load_value, match_value, match_value);
 
        omap->pdata->set_pwm(omap->dm_timer,
-                             pwm->polarity == PWM_POLARITY_INVERSED,
+                             pwm_get_polarity(pwm) == PWM_POLARITY_INVERSED,
                              true,
                              PWM_OMAP_DMTIMER_TRIGGER_OVERFLOW_AND_COMPARE);
 
index 7b8ac06781378680a6f22d906c3252dd2023f0d6..1c85ecc9e7ac076e7ad6e13333a1a6f4b15b07b6 100644 (file)
@@ -157,7 +157,7 @@ static int rcar_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
                return div;
 
        /* 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;
 
        rcar_pwm_update(rp, RCAR_PWMCR_SYNC, RCAR_PWMCR_SYNC, RCAR_PWMCR);
index 67af9f62361fdc4b712c8adafbe4e331e32af9ae..03a99a53c39e2709247ce64f21629c4ac5181b79 100644 (file)
@@ -354,7 +354,8 @@ static int sun4i_pwm_probe(struct platform_device *pdev)
        val = sun4i_pwm_readl(pwm, PWM_CTRL_REG);
        for (i = 0; i < pwm->chip.npwm; 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);
 
        return 0;
index 9c90886f41234153d044d24f94a9ae1e82efbd83..d98599249a05688137325fcf906415e14aa7aed4 100644 (file)
@@ -26,6 +26,7 @@
 struct pwm_export {
        struct device child;
        struct pwm_device *pwm;
+       struct mutex lock;
 };
 
 static struct pwm_export *child_to_pwm_export(struct device *child)
@@ -45,15 +46,20 @@ static ssize_t period_show(struct device *child,
                           char *buf)
 {
        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,
                            struct device_attribute *attr,
                            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;
        int ret;
 
@@ -61,7 +67,11 @@ static ssize_t period_store(struct device *child,
        if (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;
 }
@@ -71,15 +81,20 @@ static ssize_t duty_cycle_show(struct device *child,
                               char *buf)
 {
        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,
                                struct device_attribute *attr,
                                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;
        int ret;
 
@@ -87,7 +102,11 @@ static ssize_t duty_cycle_store(struct device *child,
        if (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;
 }
@@ -97,33 +116,46 @@ static ssize_t enable_show(struct device *child,
                           char *buf)
 {
        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,
                            struct device_attribute *attr,
                            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;
 
        ret = kstrtoint(buf, 0, &val);
        if (ret)
                return ret;
 
+       mutex_lock(&export->lock);
+
+       pwm_get_state(pwm, &state);
+
        switch (val) {
        case 0:
-               pwm_disable(pwm);
+               state.enabled = false;
                break;
        case 1:
-               ret = pwm_enable(pwm);
+               state.enabled = true;
                break;
        default:
                ret = -EINVAL;
-               break;
+               goto unlock;
        }
 
+       pwm_apply_state(pwm, &state);
+
+unlock:
+       mutex_unlock(&export->lock);
        return ret ? : size;
 }
 
@@ -133,8 +165,11 @@ static ssize_t polarity_show(struct device *child,
 {
        const struct pwm_device *pwm = child_to_pwm_device(child);
        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:
                polarity = "normal";
                break;
@@ -151,8 +186,10 @@ static ssize_t polarity_store(struct device *child,
                              struct device_attribute *attr,
                              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;
+       struct pwm_state state;
        int ret;
 
        if (sysfs_streq(buf, "normal"))
@@ -162,7 +199,11 @@ static ssize_t polarity_store(struct device *child,
        else
                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;
 }
@@ -203,6 +244,7 @@ static int pwm_export_child(struct device *parent, struct pwm_device *pwm)
        }
 
        export->pwm = pwm;
+       mutex_init(&export->lock);
 
        export->child.release = pwm_export_release;
        export->child.parent = parent;
index 35fe4825a4546012702a56efac3bc57803b93a0c..60d6c2ac87aa526b77d308532dc7289ddef85ab5 100644 (file)
@@ -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)
 {
-       unsigned int period = pwm_get_period(pchip->pwmd);
+       unsigned int period = pchip->pdata->pwm_period;
        unsigned int duty = br * period / br_max;
 
        pwm_config(pchip->pwmd, duty, period);
@@ -424,8 +424,13 @@ static int lm3630a_probe(struct i2c_client *client,
                        dev_err(&client->dev, "fail : get pwm device\n");
                        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 */
        pchip->irq = client->irq;
index daca9e6a2bb31590071cfc7100e4c1ee8ca4f1ec..e5b14f52628fdd365ac1a9318cc8ad8be7fe89ca 100644 (file)
@@ -246,6 +246,12 @@ static void lp855x_pwm_ctrl(struct lp855x *lp, int br, int max_br)
                        return;
 
                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);
index 5d583d7a517bc45ece6463a12f2b897ef513c1e2..cf869ec90cce89be06ce7884a684993c3ecaf4fe 100644 (file)
@@ -145,6 +145,12 @@ static void lp8788_pwm_ctrl(struct lp8788_bl *bl, int br, int max_br)
                }
 
                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);
index 64f9e1b8655f4b29f64f95c6cf9b3a0e9cc04c6d..b2b366bb0f9784294a287ea220777cb9cc5a9e19 100644 (file)
@@ -201,6 +201,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
        struct device_node *node = pdev->dev.of_node;
        struct pwm_bl_data *pb;
        int initial_blank = FB_BLANK_UNBLANK;
+       struct pwm_args pargs;
        int ret;
 
        if (!data) {
@@ -306,17 +307,22 @@ static int pwm_backlight_probe(struct platform_device *pdev)
 
        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
         * 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
         * via the PWM lookup table.
         */
-       pb->period = pwm_get_period(pb->pwm);
-       if (!pb->period && (data->pwm_period_ns > 0)) {
+       pwm_get_args(pb->pwm, &pargs);
+       pb->period = pargs.period;
+       if (!pb->period && (data->pwm_period_ns > 0))
                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);
 
index 21dafe53ca492421fd6fe747e32ab3f7aa643dbd..a9c45c89b15eaca20f6a62957ff92dd20775a6de 100644 (file)
@@ -286,6 +286,7 @@ static int ssd1307fb_init(struct ssd1307fb_par *par)
 {
        int ret;
        u32 precharge, dclk, com_invdir, compins;
+       struct pwm_args pargs;
 
        if (par->device_info->need_pwm) {
                par->pwm = pwm_get(&par->client->dev, NULL);
@@ -294,7 +295,15 @@ static int ssd1307fb_init(struct ssd1307fb_par *par)
                        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 */
                pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
                pwm_enable(par->pwm);
index b78d27c426290089b76546a8fc27d8b95f7216d0..17018f3c066ed5a44c76ca77ce63b6203725ef34 100644 (file)
@@ -5,59 +5,7 @@
 #include <linux/mutex.h>
 #include <linux/of.h>
 
-struct pwm_device;
 struct seq_file;
-
-#if IS_ENABLED(CONFIG_PWM)
-/*
- * pwm_request - request a PWM device
- */
-struct pwm_device *pwm_request(int pwm_id, const char *label);
-
-/*
- * pwm_free - free a PWM device
- */
-void pwm_free(struct pwm_device *pwm);
-
-/*
- * pwm_config - change a PWM device configuration
- */
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);
-
-/*
- * pwm_enable - start a PWM output toggling
- */
-int pwm_enable(struct pwm_device *pwm);
-
-/*
- * pwm_disable - stop a PWM output toggling
- */
-void pwm_disable(struct pwm_device *pwm);
-#else
-static inline struct pwm_device *pwm_request(int pwm_id, const char *label)
-{
-       return ERR_PTR(-ENODEV);
-}
-
-static inline void pwm_free(struct pwm_device *pwm)
-{
-}
-
-static inline int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
-{
-       return -EINVAL;
-}
-
-static inline int pwm_enable(struct pwm_device *pwm)
-{
-       return -EINVAL;
-}
-
-static inline void pwm_disable(struct pwm_device *pwm)
-{
-}
-#endif
-
 struct pwm_chip;
 
 /**
@@ -94,8 +42,21 @@ struct pwm_args {
 
 enum {
        PWMF_REQUESTED = 1 << 0,
-       PWMF_ENABLED = 1 << 1,
-       PWMF_EXPORTED = 1 << 2,
+       PWMF_EXPORTED = 1 << 1,
+};
+
+/*
+ * struct pwm_state - state of a PWM channel
+ * @period: PWM period (in nanoseconds)
+ * @duty_cycle: PWM duty cycle (in nanoseconds)
+ * @polarity: PWM polarity
+ * @enabled: PWM enabled status
+ */
+struct pwm_state {
+       unsigned int period;
+       unsigned int duty_cycle;
+       enum pwm_polarity polarity;
+       bool enabled;
 };
 
 /**
@@ -106,11 +67,8 @@ enum {
  * @pwm: global index of the PWM device
  * @chip: PWM chip providing this PWM device
  * @chip_data: chip-private data associated with the PWM device
- * @lock: used to serialize accesses to the PWM device where necessary
- * @period: period of the PWM signal (in nanoseconds)
- * @duty_cycle: duty cycle of the PWM signal (in nanoseconds)
- * @polarity: polarity of the PWM signal
  * @args: PWM arguments
+ * @state: curent PWM channel state
  */
 struct pwm_device {
        const char *label;
@@ -119,50 +77,68 @@ struct pwm_device {
        unsigned int pwm;
        struct pwm_chip *chip;
        void *chip_data;
-       struct mutex lock;
-
-       unsigned int period;
-       unsigned int duty_cycle;
-       enum pwm_polarity polarity;
 
        struct pwm_args args;
+       struct pwm_state state;
 };
 
+/**
+ * pwm_get_state() - retrieve the current PWM state
+ * @pwm: PWM device
+ * @state: state to fill with the current PWM state
+ */
+static inline void pwm_get_state(const struct pwm_device *pwm,
+                                struct pwm_state *state)
+{
+       *state = pwm->state;
+}
+
 static inline bool pwm_is_enabled(const struct pwm_device *pwm)
 {
-       return test_bit(PWMF_ENABLED, &pwm->flags);
+       struct pwm_state state;
+
+       pwm_get_state(pwm, &state);
+
+       return state.enabled;
 }
 
 static inline void pwm_set_period(struct pwm_device *pwm, unsigned int period)
 {
        if (pwm)
-               pwm->period = period;
+               pwm->state.period = period;
 }
 
 static inline unsigned int pwm_get_period(const struct pwm_device *pwm)
 {
-       return pwm ? pwm->period : 0;
+       struct pwm_state state;
+
+       pwm_get_state(pwm, &state);
+
+       return state.period;
 }
 
 static inline void pwm_set_duty_cycle(struct pwm_device *pwm, unsigned int duty)
 {
        if (pwm)
-               pwm->duty_cycle = duty;
+               pwm->state.duty_cycle = duty;
 }
 
 static inline unsigned int pwm_get_duty_cycle(const struct pwm_device *pwm)
 {
-       return pwm ? pwm->duty_cycle : 0;
-}
+       struct pwm_state state;
 
-/*
- * pwm_set_polarity - configure the polarity of a PWM signal
- */
-int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity);
+       pwm_get_state(pwm, &state);
+
+       return state.duty_cycle;
+}
 
 static inline enum pwm_polarity pwm_get_polarity(const struct pwm_device *pwm)
 {
-       return pwm ? pwm->polarity : PWM_POLARITY_NORMAL;
+       struct pwm_state state;
+
+       pwm_get_state(pwm, &state);
+
+       return state.polarity;
 }
 
 static inline void pwm_get_args(const struct pwm_device *pwm,
@@ -171,12 +147,6 @@ static inline void pwm_get_args(const struct pwm_device *pwm,
        *args = pwm->args;
 }
 
-static inline void pwm_apply_args(struct pwm_device *pwm)
-{
-       pwm_set_period(pwm, pwm->args.period);
-       pwm_set_polarity(pwm, pwm->args.polarity);
-}
-
 /**
  * struct pwm_ops - PWM controller operations
  * @request: optional hook for requesting a PWM
@@ -185,6 +155,13 @@ static inline void pwm_apply_args(struct pwm_device *pwm)
  * @set_polarity: configure the polarity of this PWM
  * @enable: enable PWM output toggling
  * @disable: disable PWM output toggling
+ * @apply: atomically apply a new PWM config. The state argument
+ *        should be adjusted with the real hardware config (if the
+ *        approximate the period or duty_cycle value, state should
+ *        reflect it)
+ * @get_state: get the current PWM state. This function is only
+ *            called once per PWM device when the PWM chip is
+ *            registered.
  * @dbg_show: optional routine to show contents in debugfs
  * @owner: helps prevent removal of modules exporting active PWMs
  */
@@ -197,6 +174,10 @@ struct pwm_ops {
                            enum pwm_polarity polarity);
        int (*enable)(struct pwm_chip *chip, struct pwm_device *pwm);
        void (*disable)(struct pwm_chip *chip, struct pwm_device *pwm);
+       int (*apply)(struct pwm_chip *chip, struct pwm_device *pwm,
+                    struct pwm_state *state);
+       void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm,
+                         struct pwm_state *state);
 #ifdef CONFIG_DEBUG_FS
        void (*dbg_show)(struct pwm_chip *chip, struct seq_file *s);
 #endif
@@ -232,6 +213,115 @@ struct pwm_chip {
 };
 
 #if IS_ENABLED(CONFIG_PWM)
+/* PWM user APIs */
+struct pwm_device *pwm_request(int pwm_id, const char *label);
+void pwm_free(struct pwm_device *pwm);
+int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state);
+int pwm_adjust_config(struct pwm_device *pwm);
+
+/**
+ * pwm_config() - change a PWM device configuration
+ * @pwm: PWM device
+ * @duty_ns: "on" time (in nanoseconds)
+ * @period_ns: duration (in nanoseconds) of one cycle
+ *
+ * Returns: 0 on success or a negative error code on failure.
+ */
+static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
+                            int period_ns)
+{
+       struct pwm_state state;
+
+       if (!pwm)
+               return -EINVAL;
+
+       pwm_get_state(pwm, &state);
+       if (state.duty_cycle == duty_ns && state.period == period_ns)
+               return 0;
+
+       state.duty_cycle = duty_ns;
+       state.period = period_ns;
+       return pwm_apply_state(pwm, &state);
+}
+
+/**
+ * pwm_set_polarity() - configure the polarity of a PWM signal
+ * @pwm: PWM device
+ * @polarity: new polarity of the PWM signal
+ *
+ * Note that the polarity cannot be configured while the PWM device is
+ * enabled.
+ *
+ * Returns: 0 on success or a negative error code on failure.
+ */
+static inline int pwm_set_polarity(struct pwm_device *pwm,
+                                  enum pwm_polarity polarity)
+{
+       struct pwm_state state;
+
+       if (!pwm)
+               return -EINVAL;
+
+       pwm_get_state(pwm, &state);
+       if (state.polarity == polarity)
+               return 0;
+
+       /*
+        * Changing the polarity of a running PWM without adjusting the
+        * dutycycle/period value is a bit risky (can introduce glitches).
+        * Return -EBUSY in this case.
+        * Note that this is allowed when using pwm_apply_state() because
+        * the user specifies all the parameters.
+        */
+       if (state.enabled)
+               return -EBUSY;
+
+       state.polarity = polarity;
+       return pwm_apply_state(pwm, &state);
+}
+
+/**
+ * pwm_enable() - start a PWM output toggling
+ * @pwm: PWM device
+ *
+ * Returns: 0 on success or a negative error code on failure.
+ */
+static inline int pwm_enable(struct pwm_device *pwm)
+{
+       struct pwm_state state;
+
+       if (!pwm)
+               return -EINVAL;
+
+       pwm_get_state(pwm, &state);
+       if (state.enabled)
+               return 0;
+
+       state.enabled = true;
+       return pwm_apply_state(pwm, &state);
+}
+
+/**
+ * pwm_disable() - stop a PWM output toggling
+ * @pwm: PWM device
+ */
+static inline void pwm_disable(struct pwm_device *pwm)
+{
+       struct pwm_state state;
+
+       if (!pwm)
+               return;
+
+       pwm_get_state(pwm, &state);
+       if (!state.enabled)
+               return;
+
+       state.enabled = false;
+       pwm_apply_state(pwm, &state);
+}
+
+
+/* PWM provider APIs */
 int pwm_set_chip_data(struct pwm_device *pwm, void *data);
 void *pwm_get_chip_data(struct pwm_device *pwm);
 
@@ -257,6 +347,47 @@ void devm_pwm_put(struct device *dev, struct pwm_device *pwm);
 
 bool pwm_can_sleep(struct pwm_device *pwm);
 #else
+static inline struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+       return ERR_PTR(-ENODEV);
+}
+
+static inline void pwm_free(struct pwm_device *pwm)
+{
+}
+
+static inline int pwm_apply_state(struct pwm_device *pwm,
+                                 const struct pwm_state *state)
+{
+       return -ENOTSUPP;
+}
+
+static inline int pwm_adjust_config(struct pwm_device *pwm)
+{
+       return -ENOTSUPP;
+}
+
+static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
+                            int period_ns)
+{
+       return -EINVAL;
+}
+
+static inline int pwm_set_polarity(struct pwm_device *pwm,
+                                  enum pwm_polarity polarity)
+{
+       return -ENOTSUPP;
+}
+
+static inline int pwm_enable(struct pwm_device *pwm)
+{
+       return -EINVAL;
+}
+
+static inline void pwm_disable(struct pwm_device *pwm)
+{
+}
+
 static inline int pwm_set_chip_data(struct pwm_device *pwm, void *data)
 {
        return -EINVAL;
@@ -328,6 +459,34 @@ static inline bool pwm_can_sleep(struct pwm_device *pwm)
 }
 #endif
 
+static inline void pwm_apply_args(struct pwm_device *pwm)
+{
+       /*
+        * PWM users calling pwm_apply_args() expect to have a fresh config
+        * where the polarity and period are set according to pwm_args info.
+        * The problem is, polarity can only be changed when the PWM is
+        * disabled.
+        *
+        * PWM drivers supporting hardware readout may declare the PWM device
+        * as enabled, and prevent polarity setting, which changes from the
+        * existing behavior, where all PWM devices are declared as disabled
+        * at startup (even if they are actually enabled), thus authorizing
+        * polarity setting.
+        *
+        * Instead of setting ->enabled to false, we call pwm_disable()
+        * before pwm_set_polarity() to ensure that everything is configured
+        * as expected, and the PWM is really disabled when the user request
+        * it.
+        *
+        * Note that PWM users requiring a smooth handover between the
+        * bootloader and the kernel (like critical regulators controlled by
+        * PWM devices) will have to switch to the atomic API and avoid calling
+        * pwm_apply_args().
+        */
+       pwm_disable(pwm);
+       pwm_set_polarity(pwm, pwm->args.polarity);
+}
+
 struct pwm_lookup {
        struct list_head list;
        const char *provider;