pwm: Changes for v3.13-rc1

Mostly bug fixes and clean up. There is a new driver, which is actually
 moving a custom PWM driver from drivers/misc.
 
 The majority of the patches are enhancements to the device tree support
 in the pwm-backlight driver. Backlights can now additionally be powered
 using a regulator and enabled using a GPIO in addition to just the PWM
 input.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABAgAGBQJSh1s7AAoJEN0jrNd/PrOhdS4P/1A/Tx05pP0ACsizhkB4Ro02
 XgjIusWw/jp+3LWXrlaReVNb5H6GlahDT5+2/ApAw06o7INGvpSzyI1fuFQ2DU6K
 L7MgWDSifeL7jiwfUq5f3Qv4Vccjgii0z4BBQI5ZpPluIzKpo2uhz7gIjBR3wPu9
 i34bc902SL/DrlvbMUsRHlYT9Ok3TpsPynk0pmw8xrUZDqRAX7DZH0EmvMkVsDo2
 T1vt9gQVTFVgIwlRUBcbrjbu1GnYaV20s+piQhYm1RMSUqY+ArldWKzEzlN3tLYc
 XdnkaLikdHDRBA4DeNPJGbPU8LQax8wCTaZa1PgT0PqjNX/dCkg3hsc1Em0dH9Zh
 PKvyX8R6iPdOcakkRm4qdVIacblqE1HYM7T+lyVhu6XP7MWkUmNr05HQBFjhOMcm
 whv1gS/EeB4y/FcSzn7S23UAmOvxiWfGnOMlnazrOgS00FrbC4accgSB/iXi2lDC
 szO9MHwfbwDTp8WBLEw8iTPODpq7pnFcBCw80pzblMFqC8v1MAFyriwT7BUmKcR/
 h/V75Q95VPAKq+uKsfb5Ruwa9CGKs8ACagLR0BuErT/J83Q7Q4Ezf56FUCvrGDtz
 5AWpGUakotwHtJPx6TAOgI+237MFh/GpIOrlEtz+2b5SP6ftHxr8rj+wg1sxmI2y
 JohOUIFjHtcA4UifhlTU
 =CB+z
 -----END PGP SIGNATURE-----

Merge tag 'pwm/for-3.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm

Pull pwm changes from Thierry Reding:
 "Mostly bug fixes and clean up.  There is a new driver, which is
  actually moving a custom PWM driver from drivers/misc.

  The majority of the patches are enhancements to the device tree
  support in the pwm-backlight driver.  Backlights can now additionally
  be powered using a regulator and enabled using a GPIO in addition to
  just the PWM input"

* tag 'pwm/for-3.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm: (30 commits)
  Documentation/pwm: Update supported SoC name for pwm-samsung
  pwm: samsung: Fix kernel warning while unexporting a channel
  MAINTAINERS: Move PWM subsystem tree to kernel.org
  Documentation/pwm: Fix trivial typos
  pwm-backlight: Remove unused variable
  pwm_backlight: avoid short blank screen while doing hibernation
  pwm-backlight: Fix brightness adjustment
  pwm: add ep93xx PWM support
  pwm-backlight: Allow for non-increasing brightness levels
  pwm-backlight: Add power supply support
  pwm-backlight: Use new enable_gpio field
  unicore32: Initialize PWM backlight enable_gpio field
  ARM: shmobile: Initialize PWM backlight enable_gpio field
  ARM: SAMSUNG: Initialize PWM backlight enable_gpio field
  ARM: pxa: Initialize PWM backlight enable_gpio field
  ARM: OMAP: Initialize PWM backlight enable_gpio field
  pwm-backlight: Add optional enable GPIO
  pwm-backlight: Track enable state
  pwm-backlight: Refactor backlight power on/off
  pwm-backlight: Improve readability
  ...
This commit is contained in:
Linus Torvalds 2013-11-16 12:21:40 -08:00
commit 0bde7294e2
51 changed files with 433 additions and 392 deletions

View file

@ -15,7 +15,7 @@ Required properties:
samsung,s5pc100-pwm - for 32-bit timers present on S5PC100, S5PV210,
Exynos4210 rev0 SoCs
samsung,exynos4210-pwm - for 32-bit timers present on Exynos4210,
Exynos4x12 and Exynos5250 SoCs
Exynos4x12, Exynos5250 and Exynos5420 SoCs
- reg: base address and size of register area
- interrupts: list of timer interrupts (one interrupt per timer, starting at
timer 0)

View file

@ -10,12 +10,16 @@ Required properties:
last value in the array represents a 100% duty cycle (brightest).
- default-brightness-level: the default brightness level (index into the
array defined by the "brightness-levels" property)
- power-supply: regulator for supply voltage
Optional properties:
- pwm-names: a list of names for the PWM devices specified in the
"pwms" property (see PWM binding[0])
- enable-gpios: contains a single GPIO specifier for the GPIO which enables
and disables the backlight (see GPIO binding[1])
[0]: Documentation/devicetree/bindings/pwm/pwm.txt
[1]: Documentation/devicetree/bindings/gpio/gpio.txt
Example:
@ -25,4 +29,7 @@ Example:
brightness-levels = <0 4 8 16 32 64 128 255>;
default-brightness-level = <6>;
power-supply = <&vdd_bl_reg>;
enable-gpios = <&gpio 58 0>;
};

View file

@ -39,7 +39,7 @@ New users should use the pwm_get() function and pass to it the consumer
device or a consumer name. pwm_put() is used to free the PWM device. Managed
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);
@ -94,7 +94,7 @@ for new drivers to use the generic PWM framework.
A new PWM controller/chip can be added using pwmchip_add() and removed
again with pwmchip_remove(). pwmchip_add() takes a filled in struct
pwm_chip as argument which provides a description of the PWM chip, the
number of PWM devices provider by the chip and the chip-specific
number of PWM devices provided by the chip and the chip-specific
implementation of the supported PWM operations to the framework.
Locking

View file

@ -6785,8 +6785,7 @@ PWM SUBSYSTEM
M: Thierry Reding <thierry.reding@gmail.com>
L: linux-pwm@vger.kernel.org
S: Maintained
W: http://gitorious.org/linux-pwm
T: git git://gitorious.org/linux-pwm/linux-pwm.git
T: git git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm.git
F: Documentation/pwm.txt
F: Documentation/devicetree/bindings/pwm/
F: include/linux/pwm.h

View file

@ -310,6 +310,7 @@ static struct platform_pwm_backlight_data cm_x300_backlight_data = {
.max_brightness = 100,
.dft_brightness = 100,
.pwm_period_ns = 10000,
.enable_gpio = -1,
};
static struct platform_device cm_x300_backlight_device = {

View file

@ -189,6 +189,7 @@ static struct platform_pwm_backlight_data income_backlight_data = {
.max_brightness = 0x3ff,
.dft_brightness = 0x1ff,
.pwm_period_ns = 1000000,
.enable_gpio = -1,
};
static struct platform_device income_backlight = {

View file

@ -54,6 +54,7 @@ static struct platform_pwm_backlight_data ezx_backlight_data = {
.max_brightness = 1023,
.dft_brightness = 1023,
.pwm_period_ns = 78770,
.enable_gpio = -1,
};
static struct platform_device ezx_backlight_device = {

View file

@ -561,6 +561,7 @@ static struct platform_pwm_backlight_data backlight_data = {
.max_brightness = 200,
.dft_brightness = 100,
.pwm_period_ns = 30923,
.enable_gpio = -1,
};
static struct platform_device backlight = {

View file

@ -269,6 +269,7 @@ static struct platform_pwm_backlight_data lpd270_backlight_data = {
.max_brightness = 1,
.dft_brightness = 1,
.pwm_period_ns = 78770,
.enable_gpio = -1,
};
static struct platform_device lpd270_backlight_device = {

View file

@ -378,6 +378,7 @@ static struct platform_pwm_backlight_data backlight_data = {
.max_brightness = 272,
.dft_brightness = 100,
.pwm_period_ns = 30923,
.enable_gpio = -1,
.init = magician_backlight_init,
.notify = magician_backlight_notify,
.exit = magician_backlight_exit,

View file

@ -338,6 +338,7 @@ static struct platform_pwm_backlight_data mainstone_backlight_data = {
.max_brightness = 1023,
.dft_brightness = 1023,
.pwm_period_ns = 78770,
.enable_gpio = -1,
};
static struct platform_device mainstone_backlight_device = {

View file

@ -186,6 +186,7 @@ static struct platform_pwm_backlight_data mioa701_backlight_data = {
.max_brightness = 100,
.dft_brightness = 50,
.pwm_period_ns = 4000 * 1024, /* Fl = 250kHz */
.enable_gpio = -1,
};
/*

View file

@ -322,6 +322,7 @@ static struct platform_pwm_backlight_data palm27x_backlight_data = {
.max_brightness = 0xfe,
.dft_brightness = 0x7e,
.pwm_period_ns = 3500 * 1024,
.enable_gpio = -1,
.init = palm27x_backlight_init,
.notify = palm27x_backlight_notify,
.exit = palm27x_backlight_exit,

View file

@ -166,45 +166,12 @@ static inline void palmtc_keys_init(void) {}
* Backlight
******************************************************************************/
#if defined(CONFIG_BACKLIGHT_PWM) || defined(CONFIG_BACKLIGHT_PWM_MODULE)
static int palmtc_backlight_init(struct device *dev)
{
int ret;
ret = gpio_request(GPIO_NR_PALMTC_BL_POWER, "BL POWER");
if (ret)
goto err;
ret = gpio_direction_output(GPIO_NR_PALMTC_BL_POWER, 1);
if (ret)
goto err2;
return 0;
err2:
gpio_free(GPIO_NR_PALMTC_BL_POWER);
err:
return ret;
}
static int palmtc_backlight_notify(struct device *dev, int brightness)
{
/* backlight is on when GPIO16 AF0 is high */
gpio_set_value(GPIO_NR_PALMTC_BL_POWER, brightness);
return brightness;
}
static void palmtc_backlight_exit(struct device *dev)
{
gpio_free(GPIO_NR_PALMTC_BL_POWER);
}
static struct platform_pwm_backlight_data palmtc_backlight_data = {
.pwm_id = 1,
.max_brightness = PALMTC_MAX_INTENSITY,
.dft_brightness = PALMTC_MAX_INTENSITY,
.pwm_period_ns = PALMTC_PERIOD_NS,
.init = palmtc_backlight_init,
.notify = palmtc_backlight_notify,
.exit = palmtc_backlight_exit,
.enable_gpio = GPIO_NR_PALMTC_BL_POWER,
};
static struct platform_device palmtc_backlight = {

View file

@ -165,6 +165,7 @@ static struct platform_pwm_backlight_data palmte2_backlight_data = {
.max_brightness = PALMTE2_MAX_INTENSITY,
.dft_brightness = PALMTE2_MAX_INTENSITY,
.pwm_period_ns = PALMTE2_PERIOD_NS,
.enable_gpio = -1,
.init = palmte2_backlight_init,
.notify = palmte2_backlight_notify,
.exit = palmte2_backlight_exit,

View file

@ -153,6 +153,7 @@ static struct platform_pwm_backlight_data pcm990_backlight_data = {
.max_brightness = 1023,
.dft_brightness = 1023,
.pwm_period_ns = 78770,
.enable_gpio = -1,
};
static struct platform_device pcm990_backlight_device = {

View file

@ -539,6 +539,7 @@ static struct platform_pwm_backlight_data raumfeld_pwm_backlight_data = {
.dft_brightness = 100,
/* 10000 ns = 10 ms ^= 100 kHz */
.pwm_period_ns = 10000,
.enable_gpio = -1,
};
static struct platform_device raumfeld_pwm_backlight_device = {

View file

@ -175,6 +175,7 @@ static struct platform_pwm_backlight_data tavorevb_backlight_data[] = {
.max_brightness = 100,
.dft_brightness = 100,
.pwm_period_ns = 100000,
.enable_gpio = -1,
},
[1] = {
/* secondary backlight */
@ -182,6 +183,7 @@ static struct platform_pwm_backlight_data tavorevb_backlight_data[] = {
.max_brightness = 100,
.dft_brightness = 100,
.pwm_period_ns = 100000,
.enable_gpio = -1,
},
};

View file

@ -401,6 +401,7 @@ static struct platform_pwm_backlight_data viper_backlight_data = {
.max_brightness = 100,
.dft_brightness = 100,
.pwm_period_ns = 1000000,
.enable_gpio = -1,
.init = viper_backlight_init,
.notify = viper_backlight_notify,
.exit = viper_backlight_exit,

View file

@ -206,6 +206,7 @@ static struct platform_pwm_backlight_data z2_backlight_data[] = {
.max_brightness = 1023,
.dft_brightness = 0,
.pwm_period_ns = 1260320,
.enable_gpio = -1,
},
[1] = {
/* LCD Backlight */
@ -213,6 +214,7 @@ static struct platform_pwm_backlight_data z2_backlight_data[] = {
.max_brightness = 1023,
.dft_brightness = 512,
.pwm_period_ns = 1260320,
.enable_gpio = -1,
},
};

View file

@ -125,6 +125,7 @@ static struct platform_pwm_backlight_data zylonite_backlight_data = {
.max_brightness = 100,
.dft_brightness = 100,
.pwm_period_ns = 10000,
.enable_gpio = -1,
};
static struct platform_device zylonite_backlight_device = {

View file

@ -504,6 +504,7 @@ static struct platform_pwm_backlight_data backlight_data = {
.dft_brightness = 50,
/* tcnt = 0x31 */
.pwm_period_ns = 36296,
.enable_gpio = -1,
.init = h1940_backlight_init,
.notify = h1940_backlight_notify,
.exit = h1940_backlight_exit,

View file

@ -522,6 +522,7 @@ static struct platform_pwm_backlight_data rx1950_backlight_data = {
.max_brightness = 24,
.dft_brightness = 4,
.pwm_period_ns = 48000,
.enable_gpio = -1,
.init = rx1950_backlight_init,
.notify = rx1950_backlight_notify,
.exit = rx1950_backlight_exit,

View file

@ -114,6 +114,7 @@ static struct platform_pwm_backlight_data crag6410_backlight_data = {
.max_brightness = 1000,
.dft_brightness = 600,
.pwm_period_ns = 100000, /* about 1kHz */
.enable_gpio = -1,
};
static struct platform_device crag6410_backlight_device = {

View file

@ -114,6 +114,7 @@ static struct platform_pwm_backlight_data hmt_backlight_data = {
.max_brightness = 100 * 256,
.dft_brightness = 40 * 256,
.pwm_period_ns = 1000000000 / (100 * 256 * 20),
.enable_gpio = -1,
.init = hmt_bl_init,
.notify = hmt_bl_notify,
.exit = hmt_bl_exit,

View file

@ -151,6 +151,7 @@ static struct platform_pwm_backlight_data smartq_backlight_data = {
.max_brightness = 1000,
.dft_brightness = 600,
.pwm_period_ns = 1000000000 / (1000 * 20),
.enable_gpio = -1,
.init = smartq_bl_init,
};

View file

@ -625,6 +625,7 @@ static struct samsung_bl_gpio_info smdk6410_bl_gpio_info = {
static struct platform_pwm_backlight_data smdk6410_bl_data = {
.pwm_id = 1,
.enable_gpio = -1,
};
static struct s3c_hsotg_plat smdk6410_hsotg_pdata;

View file

@ -223,6 +223,7 @@ static struct samsung_bl_gpio_info smdk6440_bl_gpio_info = {
static struct platform_pwm_backlight_data smdk6440_bl_data = {
.pwm_id = 1,
.enable_gpio = -1,
};
static void __init smdk6440_map_io(void)

View file

@ -242,6 +242,7 @@ static struct samsung_bl_gpio_info smdk6450_bl_gpio_info = {
static struct platform_pwm_backlight_data smdk6450_bl_data = {
.pwm_id = 1,
.enable_gpio = -1,
};
static void __init smdk6450_map_io(void)

View file

@ -216,6 +216,7 @@ static struct samsung_bl_gpio_info smdkc100_bl_gpio_info = {
static struct platform_pwm_backlight_data smdkc100_bl_data = {
.pwm_id = 0,
.enable_gpio = -1,
};
static void __init smdkc100_map_io(void)

View file

@ -279,6 +279,7 @@ static struct samsung_bl_gpio_info smdkv210_bl_gpio_info = {
static struct platform_pwm_backlight_data smdkv210_bl_data = {
.pwm_id = 3,
.pwm_period_ns = 1000,
.enable_gpio = -1,
};
static void __init smdkv210_map_io(void)

View file

@ -423,6 +423,7 @@ static struct platform_pwm_backlight_data pwm_backlight_data = {
.max_brightness = 255,
.dft_brightness = 255,
.pwm_period_ns = 33333, /* 30kHz */
.enable_gpio = -1,
};
static struct platform_device pwm_backlight_device = {

View file

@ -70,6 +70,7 @@ static struct samsung_bl_drvdata samsung_dfl_bl_data __initdata = {
.max_brightness = 255,
.dft_brightness = 255,
.pwm_period_ns = 78770,
.enable_gpio = -1,
.init = samsung_bl_init,
.exit = samsung_bl_exit,
},
@ -121,6 +122,10 @@ void __init samsung_bl_set(struct samsung_bl_gpio_info *gpio_info,
samsung_bl_data->lth_brightness = bl_data->lth_brightness;
if (bl_data->pwm_period_ns)
samsung_bl_data->pwm_period_ns = bl_data->pwm_period_ns;
if (bl_data->enable_gpio >= 0)
samsung_bl_data->enable_gpio = bl_data->enable_gpio;
if (bl_data->enable_gpio_flags)
samsung_bl_data->enable_gpio_flags = bl_data->enable_gpio_flags;
if (bl_data->init)
samsung_bl_data->init = bl_data->init;
if (bl_data->notify)

View file

@ -54,6 +54,7 @@ static struct platform_pwm_backlight_data nb0916_backlight_data = {
.max_brightness = 100,
.dft_brightness = 100,
.pwm_period_ns = 70 * 1024,
.enable_gpio = -1,
};
static struct gpio_keys_button nb0916_gpio_keys[] = {

View file

@ -381,19 +381,6 @@ config HMC6352
This driver provides support for the Honeywell HMC6352 compass,
providing configuration and heading data via sysfs.
config EP93XX_PWM
tristate "EP93xx PWM support"
depends on ARCH_EP93XX
help
This option enables device driver support for the PWM channels
on the Cirrus EP93xx processors. The EP9307 chip only has one
PWM channel all the others have two, the second channel is an
alternate function of the EGPIO14 pin. A sysfs interface is
provided to control the PWM channels.
To compile this driver as a module, choose M here: the module will
be called ep93xx_pwm.
config DS1682
tristate "Dallas DS1682 Total Elapsed Time Recorder with Alarm"
depends on I2C

View file

@ -33,7 +33,6 @@ obj-$(CONFIG_APDS9802ALS) += apds9802als.o
obj-$(CONFIG_ISL29003) += isl29003.o
obj-$(CONFIG_ISL29020) += isl29020.o
obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o
obj-$(CONFIG_DS1682) += ds1682.o
obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o
obj-$(CONFIG_C2PORT) += c2port/

View file

@ -1,286 +0,0 @@
/*
* Simple PWM driver for EP93XX
*
* (c) Copyright 2009 Matthieu Crapet <mcrapet@gmail.com>
* (c) Copyright 2009 H Hartley Sweeten <hsweeten@visionengravers.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* EP9307 has only one channel:
* - PWMOUT
*
* EP9301/02/12/15 have two channels:
* - PWMOUT
* - PWMOUT1 (alternate function for EGPIO14)
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <mach/platform.h>
#define EP93XX_PWMx_TERM_COUNT 0x00
#define EP93XX_PWMx_DUTY_CYCLE 0x04
#define EP93XX_PWMx_ENABLE 0x08
#define EP93XX_PWMx_INVERT 0x0C
#define EP93XX_PWM_MAX_COUNT 0xFFFF
struct ep93xx_pwm {
void __iomem *mmio_base;
struct clk *clk;
u32 duty_percent;
};
/*
* /sys/devices/platform/ep93xx-pwm.N
* /min_freq read-only minimum pwm output frequency
* /max_req read-only maximum pwm output frequency
* /freq read-write pwm output frequency (0 = disable output)
* /duty_percent read-write pwm duty cycle percent (1..99)
* /invert read-write invert pwm output
*/
static ssize_t ep93xx_pwm_get_min_freq(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
unsigned long rate = clk_get_rate(pwm->clk);
return sprintf(buf, "%ld\n", rate / (EP93XX_PWM_MAX_COUNT + 1));
}
static ssize_t ep93xx_pwm_get_max_freq(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
unsigned long rate = clk_get_rate(pwm->clk);
return sprintf(buf, "%ld\n", rate / 2);
}
static ssize_t ep93xx_pwm_get_freq(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
if (readl(pwm->mmio_base + EP93XX_PWMx_ENABLE) & 0x1) {
unsigned long rate = clk_get_rate(pwm->clk);
u16 term = readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT);
return sprintf(buf, "%ld\n", rate / (term + 1));
} else {
return sprintf(buf, "disabled\n");
}
}
static ssize_t ep93xx_pwm_set_freq(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct platform_device *pdev = to_platform_device(dev);
struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
long val;
int err;
err = kstrtol(buf, 10, &val);
if (err)
return -EINVAL;
if (val == 0) {
writel(0x0, pwm->mmio_base + EP93XX_PWMx_ENABLE);
} else if (val <= (clk_get_rate(pwm->clk) / 2)) {
u32 term, duty;
val = (clk_get_rate(pwm->clk) / val) - 1;
if (val > EP93XX_PWM_MAX_COUNT)
val = EP93XX_PWM_MAX_COUNT;
if (val < 1)
val = 1;
term = readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT);
duty = ((val + 1) * pwm->duty_percent / 100) - 1;
/* If pwm is running, order is important */
if (val > term) {
writel(val, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT);
writel(duty, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE);
} else {
writel(duty, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE);
writel(val, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT);
}
if (!readl(pwm->mmio_base + EP93XX_PWMx_ENABLE) & 0x1)
writel(0x1, pwm->mmio_base + EP93XX_PWMx_ENABLE);
} else {
return -EINVAL;
}
return count;
}
static ssize_t ep93xx_pwm_get_duty_percent(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
return sprintf(buf, "%d\n", pwm->duty_percent);
}
static ssize_t ep93xx_pwm_set_duty_percent(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct platform_device *pdev = to_platform_device(dev);
struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
long val;
int err;
err = kstrtol(buf, 10, &val);
if (err)
return -EINVAL;
if (val > 0 && val < 100) {
u32 term = readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT);
u32 duty = ((term + 1) * val / 100) - 1;
writel(duty, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE);
pwm->duty_percent = val;
return count;
}
return -EINVAL;
}
static ssize_t ep93xx_pwm_get_invert(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
int inverted = readl(pwm->mmio_base + EP93XX_PWMx_INVERT) & 0x1;
return sprintf(buf, "%d\n", inverted);
}
static ssize_t ep93xx_pwm_set_invert(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct platform_device *pdev = to_platform_device(dev);
struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
long val;
int err;
err = kstrtol(buf, 10, &val);
if (err)
return -EINVAL;
if (val == 0)
writel(0x0, pwm->mmio_base + EP93XX_PWMx_INVERT);
else if (val == 1)
writel(0x1, pwm->mmio_base + EP93XX_PWMx_INVERT);
else
return -EINVAL;
return count;
}
static DEVICE_ATTR(min_freq, S_IRUGO, ep93xx_pwm_get_min_freq, NULL);
static DEVICE_ATTR(max_freq, S_IRUGO, ep93xx_pwm_get_max_freq, NULL);
static DEVICE_ATTR(freq, S_IWUSR | S_IRUGO,
ep93xx_pwm_get_freq, ep93xx_pwm_set_freq);
static DEVICE_ATTR(duty_percent, S_IWUSR | S_IRUGO,
ep93xx_pwm_get_duty_percent, ep93xx_pwm_set_duty_percent);
static DEVICE_ATTR(invert, S_IWUSR | S_IRUGO,
ep93xx_pwm_get_invert, ep93xx_pwm_set_invert);
static struct attribute *ep93xx_pwm_attrs[] = {
&dev_attr_min_freq.attr,
&dev_attr_max_freq.attr,
&dev_attr_freq.attr,
&dev_attr_duty_percent.attr,
&dev_attr_invert.attr,
NULL
};
static const struct attribute_group ep93xx_pwm_sysfs_files = {
.attrs = ep93xx_pwm_attrs,
};
static int ep93xx_pwm_probe(struct platform_device *pdev)
{
struct ep93xx_pwm *pwm;
struct resource *res;
int ret;
pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
if (!pwm)
return -ENOMEM;
pwm->clk = devm_clk_get(&pdev->dev, "pwm_clk");
if (IS_ERR(pwm->clk))
return PTR_ERR(pwm->clk);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pwm->mmio_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(pwm->mmio_base))
return PTR_ERR(pwm->mmio_base);
ret = ep93xx_pwm_acquire_gpio(pdev);
if (ret)
return ret;
ret = sysfs_create_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files);
if (ret) {
ep93xx_pwm_release_gpio(pdev);
return ret;
}
pwm->duty_percent = 50;
/* disable pwm at startup. Avoids zero value. */
writel(0x0, pwm->mmio_base + EP93XX_PWMx_ENABLE);
writel(EP93XX_PWM_MAX_COUNT, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT);
writel(EP93XX_PWM_MAX_COUNT/2, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE);
clk_enable(pwm->clk);
platform_set_drvdata(pdev, pwm);
return 0;
}
static int ep93xx_pwm_remove(struct platform_device *pdev)
{
struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
writel(0x0, pwm->mmio_base + EP93XX_PWMx_ENABLE);
clk_disable(pwm->clk);
sysfs_remove_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files);
ep93xx_pwm_release_gpio(pdev);
return 0;
}
static struct platform_driver ep93xx_pwm_driver = {
.driver = {
.name = "ep93xx-pwm",
.owner = THIS_MODULE,
},
.probe = ep93xx_pwm_probe,
.remove = ep93xx_pwm_remove,
};
module_platform_driver(ep93xx_pwm_driver);
MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>, "
"H Hartley Sweeten <hsweeten@visionengravers.com>");
MODULE_DESCRIPTION("EP93xx PWM driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:ep93xx-pwm");

View file

@ -62,6 +62,15 @@ config PWM_BFIN
To compile this driver as a module, choose M here: the module
will be called pwm-bfin.
config PWM_EP93XX
tristate "Cirrus Logic EP93xx PWM support"
depends on ARCH_EP93XX
help
Generic PWM framework driver for Cirrus Logic EP93xx.
To compile this driver as a module, choose M here: the module
will be called pwm-ep93xx.
config PWM_IMX
tristate "i.MX PWM support"
depends on ARCH_MXC

View file

@ -3,6 +3,7 @@ obj-$(CONFIG_PWM_SYSFS) += sysfs.o
obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o
obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o
obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o
obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o
obj-$(CONFIG_PWM_IMX) += pwm-imx.o
obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o
obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o

View file

@ -249,6 +249,8 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
}
}
cmr |= (tcbpwm->div & ATMEL_TC_TCCLKS);
__raw_writel(cmr, regs + ATMEL_TC_REG(group, CMR));
if (index == 0)
@ -305,7 +307,7 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
i = slowclk;
rate = 32768;
min = div_u64(NSEC_PER_SEC, rate);
max = min << 16;
max = min << tc->tcb_config->counter_width;
/* If period is too big return ERANGE error */
if (max < period_ns)

230
drivers/pwm/pwm-ep93xx.c Normal file
View file

@ -0,0 +1,230 @@
/*
* PWM framework driver for Cirrus Logic EP93xx
*
* Copyright (c) 2009 Matthieu Crapet <mcrapet@gmail.com>
* Copyright (c) 2009, 2013 H Hartley Sweeten <hsweeten@visionengravers.com>
*
* EP9301/02 have only one channel:
* platform device ep93xx-pwm.1 - PWMOUT1 (EGPIO14)
*
* EP9307 has only one channel:
* platform device ep93xx-pwm.0 - PWMOUT
*
* EP9312/15 have two channels:
* platform device ep93xx-pwm.0 - PWMOUT
* platform device ep93xx-pwm.1 - PWMOUT1 (EGPIO14)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/pwm.h>
#include <asm/div64.h>
#include <mach/platform.h> /* for ep93xx_pwm_{acquire,release}_gpio() */
#define EP93XX_PWMx_TERM_COUNT 0x00
#define EP93XX_PWMx_DUTY_CYCLE 0x04
#define EP93XX_PWMx_ENABLE 0x08
#define EP93XX_PWMx_INVERT 0x0c
struct ep93xx_pwm {
void __iomem *base;
struct clk *clk;
struct pwm_chip chip;
};
static inline struct ep93xx_pwm *to_ep93xx_pwm(struct pwm_chip *chip)
{
return container_of(chip, struct ep93xx_pwm, chip);
}
static int ep93xx_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct platform_device *pdev = to_platform_device(chip->dev);
return ep93xx_pwm_acquire_gpio(pdev);
}
static void ep93xx_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct platform_device *pdev = to_platform_device(chip->dev);
ep93xx_pwm_release_gpio(pdev);
}
static int ep93xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns)
{
struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip);
void __iomem *base = ep93xx_pwm->base;
unsigned long long c;
unsigned long period_cycles;
unsigned long duty_cycles;
unsigned long term;
int ret = 0;
/*
* The clock needs to be enabled to access the PWM registers.
* Configuration can be changed at any time.
*/
if (!test_bit(PWMF_ENABLED, &pwm->flags)) {
ret = clk_enable(ep93xx_pwm->clk);
if (ret)
return ret;
}
c = clk_get_rate(ep93xx_pwm->clk);
c *= period_ns;
do_div(c, 1000000000);
period_cycles = c;
c = period_cycles;
c *= duty_ns;
do_div(c, period_ns);
duty_cycles = c;
if (period_cycles < 0x10000 && duty_cycles < 0x10000) {
term = readw(base + EP93XX_PWMx_TERM_COUNT);
/* Order is important if PWM is running */
if (period_cycles > term) {
writew(period_cycles, base + EP93XX_PWMx_TERM_COUNT);
writew(duty_cycles, base + EP93XX_PWMx_DUTY_CYCLE);
} else {
writew(duty_cycles, base + EP93XX_PWMx_DUTY_CYCLE);
writew(period_cycles, base + EP93XX_PWMx_TERM_COUNT);
}
} else {
ret = -EINVAL;
}
if (!test_bit(PWMF_ENABLED, &pwm->flags))
clk_disable(ep93xx_pwm->clk);
return ret;
}
static int ep93xx_pwm_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
enum pwm_polarity polarity)
{
struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip);
int ret;
/*
* The clock needs to be enabled to access the PWM registers.
* Polarity can only be changed when the PWM is disabled.
*/
ret = clk_enable(ep93xx_pwm->clk);
if (ret)
return ret;
if (polarity == PWM_POLARITY_INVERSED)
writew(0x1, ep93xx_pwm->base + EP93XX_PWMx_INVERT);
else
writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_INVERT);
clk_disable(ep93xx_pwm->clk);
return 0;
}
static int ep93xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip);
int ret;
ret = clk_enable(ep93xx_pwm->clk);
if (ret)
return ret;
writew(0x1, ep93xx_pwm->base + EP93XX_PWMx_ENABLE);
return 0;
}
static void ep93xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip);
writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_ENABLE);
clk_disable(ep93xx_pwm->clk);
}
static const struct pwm_ops ep93xx_pwm_ops = {
.request = ep93xx_pwm_request,
.free = ep93xx_pwm_free,
.config = ep93xx_pwm_config,
.set_polarity = ep93xx_pwm_polarity,
.enable = ep93xx_pwm_enable,
.disable = ep93xx_pwm_disable,
.owner = THIS_MODULE,
};
static int ep93xx_pwm_probe(struct platform_device *pdev)
{
struct ep93xx_pwm *ep93xx_pwm;
struct resource *res;
int ret;
ep93xx_pwm = devm_kzalloc(&pdev->dev, sizeof(*ep93xx_pwm), GFP_KERNEL);
if (!ep93xx_pwm)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ep93xx_pwm->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(ep93xx_pwm->base))
return PTR_ERR(ep93xx_pwm->base);
ep93xx_pwm->clk = devm_clk_get(&pdev->dev, "pwm_clk");
if (IS_ERR(ep93xx_pwm->clk))
return PTR_ERR(ep93xx_pwm->clk);
ep93xx_pwm->chip.dev = &pdev->dev;
ep93xx_pwm->chip.ops = &ep93xx_pwm_ops;
ep93xx_pwm->chip.base = -1;
ep93xx_pwm->chip.npwm = 1;
ret = pwmchip_add(&ep93xx_pwm->chip);
if (ret < 0)
return ret;
platform_set_drvdata(pdev, ep93xx_pwm);
return 0;
}
static int ep93xx_pwm_remove(struct platform_device *pdev)
{
struct ep93xx_pwm *ep93xx_pwm = platform_get_drvdata(pdev);
return pwmchip_remove(&ep93xx_pwm->chip);
}
static struct platform_driver ep93xx_pwm_driver = {
.driver = {
.name = "ep93xx-pwm",
},
.probe = ep93xx_pwm_probe,
.remove = ep93xx_pwm_remove,
};
module_platform_driver(ep93xx_pwm_driver);
MODULE_DESCRIPTION("Cirrus Logic EP93xx PWM driver");
MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>, "
"H Hartley Sweeten <hsweeten@visionengravers.com>");
MODULE_ALIAS("platform:ep93xx-pwm");
MODULE_LICENSE("GPL");

View file

@ -16,6 +16,7 @@
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/pwm.h>
#include <linux/of.h>
#include <linux/of_device.h>
/* i.MX1 and i.MX21 share the same PWM function block: */
@ -296,7 +297,7 @@ static struct platform_driver imx_pwm_driver = {
.driver = {
.name = "imx-pwm",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(imx_pwm_dt_ids),
.of_match_table = imx_pwm_dt_ids,
},
.probe = imx_pwm_probe,
.remove = imx_pwm_remove,

View file

@ -169,7 +169,7 @@ static struct platform_driver lpc32xx_pwm_driver = {
.driver = {
.name = "lpc32xx-pwm",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(lpc32xx_pwm_dt_ids),
.of_match_table = lpc32xx_pwm_dt_ids,
},
.probe = lpc32xx_pwm_probe,
.remove = lpc32xx_pwm_remove,

View file

@ -189,7 +189,7 @@ static struct platform_driver mxs_pwm_driver = {
.driver = {
.name = "mxs-pwm",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(mxs_pwm_dt_ids),
.of_match_table = mxs_pwm_dt_ids,
},
.probe = mxs_pwm_probe,
.remove = mxs_pwm_remove,

View file

@ -18,6 +18,7 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/slab.h>
@ -224,8 +225,8 @@ static int pwm_samsung_request(struct pwm_chip *chip, struct pwm_device *pwm)
static void pwm_samsung_free(struct pwm_chip *chip, struct pwm_device *pwm)
{
pwm_set_chip_data(pwm, NULL);
devm_kfree(chip->dev, pwm_get_chip_data(pwm));
pwm_set_chip_data(pwm, NULL);
}
static int pwm_samsung_enable(struct pwm_chip *chip, struct pwm_device *pwm)

View file

@ -26,7 +26,6 @@
#include <linux/pm_runtime.h>
#include <linux/pwm.h>
#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
#include "pwm-tipwmss.h"
@ -208,11 +207,6 @@ static int ecap_pwm_probe(struct platform_device *pdev)
struct clk *clk;
struct ecap_pwm_chip *pc;
u16 status;
struct pinctrl *pinctrl;
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR(pinctrl))
dev_warn(&pdev->dev, "unable to select pin group\n");
pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
if (!pc) {

View file

@ -26,7 +26,6 @@
#include <linux/clk.h>
#include <linux/pm_runtime.h>
#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
#include "pwm-tipwmss.h"
@ -439,11 +438,6 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev)
struct clk *clk;
struct ehrpwm_pwm_chip *pc;
u16 status;
struct pinctrl *pinctrl;
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR(pinctrl))
dev_warn(&pdev->dev, "unable to select pin group\n");
pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
if (!pc) {

View file

@ -21,6 +21,7 @@
*/
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/i2c/twl.h>

View file

@ -18,6 +18,7 @@
*/
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/i2c/twl.h>

View file

@ -10,6 +10,8 @@
* published by the Free Software Foundation.
*/
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
@ -19,6 +21,7 @@
#include <linux/err.h>
#include <linux/pwm.h>
#include <linux/pwm_backlight.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
struct pwm_bl_data {
@ -27,6 +30,11 @@ struct pwm_bl_data {
unsigned int period;
unsigned int lth_brightness;
unsigned int *levels;
bool enabled;
struct regulator *power_supply;
int enable_gpio;
unsigned long enable_gpio_flags;
unsigned int scale;
int (*notify)(struct device *,
int brightness);
void (*notify_after)(struct device *,
@ -35,11 +43,65 @@ struct pwm_bl_data {
void (*exit)(struct device *);
};
static void pwm_backlight_power_on(struct pwm_bl_data *pb, int brightness)
{
int err;
if (pb->enabled)
return;
err = regulator_enable(pb->power_supply);
if (err < 0)
dev_err(pb->dev, "failed to enable power supply\n");
if (gpio_is_valid(pb->enable_gpio)) {
if (pb->enable_gpio_flags & PWM_BACKLIGHT_GPIO_ACTIVE_LOW)
gpio_set_value(pb->enable_gpio, 0);
else
gpio_set_value(pb->enable_gpio, 1);
}
pwm_enable(pb->pwm);
pb->enabled = true;
}
static void pwm_backlight_power_off(struct pwm_bl_data *pb)
{
if (!pb->enabled)
return;
pwm_config(pb->pwm, 0, pb->period);
pwm_disable(pb->pwm);
if (gpio_is_valid(pb->enable_gpio)) {
if (pb->enable_gpio_flags & PWM_BACKLIGHT_GPIO_ACTIVE_LOW)
gpio_set_value(pb->enable_gpio, 1);
else
gpio_set_value(pb->enable_gpio, 0);
}
regulator_disable(pb->power_supply);
pb->enabled = false;
}
static int compute_duty_cycle(struct pwm_bl_data *pb, int brightness)
{
unsigned int lth = pb->lth_brightness;
int duty_cycle;
if (pb->levels)
duty_cycle = pb->levels[brightness];
else
duty_cycle = brightness;
return (duty_cycle * (pb->period - lth) / pb->scale) + lth;
}
static int pwm_backlight_update_status(struct backlight_device *bl)
{
struct pwm_bl_data *pb = bl_get_data(bl);
int brightness = bl->props.brightness;
int max = bl->props.max_brightness;
int duty_cycle;
if (bl->props.power != FB_BLANK_UNBLANK ||
bl->props.fb_blank != FB_BLANK_UNBLANK ||
@ -49,24 +111,12 @@ static int pwm_backlight_update_status(struct backlight_device *bl)
if (pb->notify)
brightness = pb->notify(pb->dev, brightness);
if (brightness == 0) {
pwm_config(pb->pwm, 0, pb->period);
pwm_disable(pb->pwm);
} else {
int duty_cycle;
if (pb->levels) {
duty_cycle = pb->levels[brightness];
max = pb->levels[max];
} else {
duty_cycle = brightness;
}
duty_cycle = pb->lth_brightness +
(duty_cycle * (pb->period - pb->lth_brightness) / max);
if (brightness > 0) {
duty_cycle = compute_duty_cycle(pb, brightness);
pwm_config(pb->pwm, duty_cycle, pb->period);
pwm_enable(pb->pwm);
}
pwm_backlight_power_on(pb, brightness);
} else
pwm_backlight_power_off(pb);
if (pb->notify_after)
pb->notify_after(pb->dev, brightness);
@ -98,6 +148,7 @@ static int pwm_backlight_parse_dt(struct device *dev,
struct platform_pwm_backlight_data *data)
{
struct device_node *node = dev->of_node;
enum of_gpio_flags flags;
struct property *prop;
int length;
u32 value;
@ -138,11 +189,13 @@ static int pwm_backlight_parse_dt(struct device *dev,
data->max_brightness--;
}
/*
* TODO: Most users of this driver use a number of GPIOs to control
* backlight power. Support for specifying these needs to be
* added.
*/
data->enable_gpio = of_get_named_gpio_flags(node, "enable-gpios", 0,
&flags);
if (data->enable_gpio == -EPROBE_DEFER)
return -EPROBE_DEFER;
if (gpio_is_valid(data->enable_gpio) && (flags & OF_GPIO_ACTIVE_LOW))
data->enable_gpio_flags |= PWM_BACKLIGHT_GPIO_ACTIVE_LOW;
return 0;
}
@ -168,7 +221,6 @@ static int pwm_backlight_probe(struct platform_device *pdev)
struct backlight_properties props;
struct backlight_device *bl;
struct pwm_bl_data *pb;
unsigned int max;
int ret;
if (!data) {
@ -195,16 +247,46 @@ static int pwm_backlight_probe(struct platform_device *pdev)
}
if (data->levels) {
max = data->levels[data->max_brightness];
unsigned int i;
for (i = 0; i <= data->max_brightness; i++)
if (data->levels[i] > pb->scale)
pb->scale = data->levels[i];
pb->levels = data->levels;
} else
max = data->max_brightness;
pb->scale = data->max_brightness;
pb->enable_gpio = data->enable_gpio;
pb->enable_gpio_flags = data->enable_gpio_flags;
pb->notify = data->notify;
pb->notify_after = data->notify_after;
pb->check_fb = data->check_fb;
pb->exit = data->exit;
pb->dev = &pdev->dev;
pb->enabled = false;
if (gpio_is_valid(pb->enable_gpio)) {
unsigned long flags;
if (pb->enable_gpio_flags & PWM_BACKLIGHT_GPIO_ACTIVE_LOW)
flags = GPIOF_OUT_INIT_HIGH;
else
flags = GPIOF_OUT_INIT_LOW;
ret = gpio_request_one(pb->enable_gpio, flags, "enable");
if (ret < 0) {
dev_err(&pdev->dev, "failed to request GPIO#%d: %d\n",
pb->enable_gpio, ret);
goto err_alloc;
}
}
pb->power_supply = devm_regulator_get(&pdev->dev, "power");
if (IS_ERR(pb->power_supply)) {
ret = PTR_ERR(pb->power_supply);
goto err_gpio;
}
pb->pwm = devm_pwm_get(&pdev->dev, NULL);
if (IS_ERR(pb->pwm)) {
@ -214,7 +296,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
if (IS_ERR(pb->pwm)) {
dev_err(&pdev->dev, "unable to request legacy PWM\n");
ret = PTR_ERR(pb->pwm);
goto err_alloc;
goto err_gpio;
}
}
@ -229,7 +311,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
pwm_set_period(pb->pwm, data->pwm_period_ns);
pb->period = pwm_get_period(pb->pwm);
pb->lth_brightness = data->lth_brightness * (pb->period / max);
pb->lth_brightness = data->lth_brightness * (pb->period / pb->scale);
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_RAW;
@ -239,7 +321,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
if (IS_ERR(bl)) {
dev_err(&pdev->dev, "failed to register backlight\n");
ret = PTR_ERR(bl);
goto err_alloc;
goto err_gpio;
}
if (data->dft_brightness > data->max_brightness) {
@ -255,6 +337,9 @@ static int pwm_backlight_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, bl);
return 0;
err_gpio:
if (gpio_is_valid(pb->enable_gpio))
gpio_free(pb->enable_gpio);
err_alloc:
if (data->exit)
data->exit(&pdev->dev);
@ -267,10 +352,11 @@ static int pwm_backlight_remove(struct platform_device *pdev)
struct pwm_bl_data *pb = bl_get_data(bl);
backlight_device_unregister(bl);
pwm_config(pb->pwm, 0, pb->period);
pwm_disable(pb->pwm);
pwm_backlight_power_off(pb);
if (pb->exit)
pb->exit(&pdev->dev);
return 0;
}
@ -282,10 +368,12 @@ static int pwm_backlight_suspend(struct device *dev)
if (pb->notify)
pb->notify(pb->dev, 0);
pwm_config(pb->pwm, 0, pb->period);
pwm_disable(pb->pwm);
pwm_backlight_power_off(pb);
if (pb->notify_after)
pb->notify_after(pb->dev, 0);
return 0;
}
@ -294,12 +382,19 @@ static int pwm_backlight_resume(struct device *dev)
struct backlight_device *bl = dev_get_drvdata(dev);
backlight_update_status(bl);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(pwm_backlight_pm_ops, pwm_backlight_suspend,
pwm_backlight_resume);
static const struct dev_pm_ops pwm_backlight_pm_ops = {
#ifdef CONFIG_PM_SLEEP
.suspend = pwm_backlight_suspend,
.resume = pwm_backlight_resume,
.poweroff = pwm_backlight_suspend,
.restore = pwm_backlight_resume,
#endif
};
static struct platform_driver pwm_backlight_driver = {
.driver = {
@ -317,4 +412,3 @@ module_platform_driver(pwm_backlight_driver);
MODULE_DESCRIPTION("PWM based Backlight Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:pwm-backlight");

View file

@ -6,6 +6,9 @@
#include <linux/backlight.h>
/* TODO: convert to gpiod_*() API once it has been merged */
#define PWM_BACKLIGHT_GPIO_ACTIVE_LOW (1 << 0)
struct platform_pwm_backlight_data {
int pwm_id;
unsigned int max_brightness;
@ -13,6 +16,8 @@ struct platform_pwm_backlight_data {
unsigned int lth_brightness;
unsigned int pwm_period_ns;
unsigned int *levels;
int enable_gpio;
unsigned long enable_gpio_flags;
int (*init)(struct device *dev);
int (*notify)(struct device *dev, int brightness);
void (*notify_after)(struct device *dev, int brightness);