2010-08-26 07:14:32

by Arun MURTHY

[permalink] [raw]
Subject: [PATCH 0/3] u8500: led and backlight driver

Arun Murthy (3):
mfd: ab8500-pwm: ab8500 pwm management
backlight: u8500: backlight driver based on ab8500 pwm
leds: ab8500-led: led driver based on ab8500 pwm

drivers/leds/Kconfig | 9 ++
drivers/leds/Makefile | 1 +
drivers/leds/leds-ab8500.c | 201 +++++++++++++++++++++++++++++++
drivers/mfd/Kconfig | 8 ++
drivers/mfd/Makefile | 1 +
drivers/mfd/ab8500-core.c | 7 +-
drivers/mfd/ab8500-pwm.c | 191 ++++++++++++++++++++++++++++++
drivers/video/backlight/Kconfig | 10 ++
drivers/video/backlight/Makefile | 2 +-
drivers/video/backlight/ab8500_bl.c | 209 +++++++++++++++++++++++++++++++++
include/linux/mfd/ab8500.h | 5 +
include/linux/mfd/abx500/ab8500-pwm.h | 32 +++++
12 files changed, 674 insertions(+), 2 deletions(-)
create mode 100644 drivers/leds/leds-ab8500.c
create mode 100644 drivers/mfd/ab8500-pwm.c
create mode 100644 drivers/video/backlight/ab8500_bl.c
create mode 100644 include/linux/mfd/abx500/ab8500-pwm.h


2010-08-26 07:14:19

by Arun MURTHY

[permalink] [raw]
Subject: [PATCH 3/3] leds: ab8500-led: led driver based on ab8500 pwm

This patch adds led class driver for controlling u8500 leds and
backlight. LED intensity is controlled by by Ananlog Baseband Chip
AB8500 Pulse Width Modulation(pwm).

Signed-off-by: Arun Murthy <[email protected]>
Acked-by: Mattias Wallin <[email protected]>
Acked-by: Linus Walleij <[email protected]>
---
drivers/leds/Kconfig | 9 ++
drivers/leds/Makefile | 1 +
drivers/leds/leds-ab8500.c | 201 ++++++++++++++++++++++++++++++++++++++++++++
drivers/mfd/ab8500-core.c | 3 +
include/linux/mfd/ab8500.h | 1 +
5 files changed, 215 insertions(+), 0 deletions(-)
create mode 100644 drivers/leds/leds-ab8500.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index e411262..8e554af 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -26,6 +26,15 @@ config LEDS_88PM860X
This option enables support for on-chip LED drivers found on Marvell
Semiconductor 88PM8606 PMIC.

+config LEDS_AB8500
+ bool "ab8500-pwm: led driver"
+ depends on LEDS_CLASS && AB8500_CORE
+ select AB8500_PWM
+ help
+ This option enables led class driver support for ab8500 pwm devices.
+ If in doubt, it's safe to enable this option; it doesn't kick
+ in unless the board's description says it's wired that way.
+
config LEDS_ATMEL_PWM
tristate "LED Support using Atmel PWM outputs"
depends on ATMEL_PWM
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 7d6b958..7ba6448 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_LEDS_ADP5520) += leds-adp5520.o
obj-$(CONFIG_LEDS_DELL_NETBOOKS) += dell-led.o
obj-$(CONFIG_LEDS_MC13783) += leds-mc13783.o
obj-$(CONFIG_LEDS_NS2) += leds-ns2.o
+obj-$(CONFIG_LEDS_AB8500) += leds-ab8500.o

# LED SPI Drivers
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
diff --git a/drivers/leds/leds-ab8500.c b/drivers/leds/leds-ab8500.c
new file mode 100644
index 0000000..9a0a9e9
--- /dev/null
+++ b/drivers/leds/leds-ab8500.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Dushyanth S R <[email protected]>
+ * Author: Arun R Murthy <[email protected]>
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/mfd/ab8500.h>
+#include <linux/mfd/abx500/ab8500-pwm.h>
+
+struct ab8500_led {
+ struct led_classdev ab8500_led_cdev;
+ struct ab8500_pwm_pdata *pwm;
+ struct device *dev;
+};
+
+static void ab8500_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct ab8500_led *led = container_of(led_cdev,
+ struct ab8500_led,
+ ab8500_led_cdev);
+ int cnt;
+
+ if (value > led_cdev->max_brightness)
+ value = led_cdev->max_brightness;
+
+ for (cnt = 0; cnt < MAX_PWM_CTRL; cnt++) {
+ if (!led->pwm->pwm_no[cnt])
+ break;
+ ab8500_pwm_set_int(led->dev,
+ led_cdev->max_brightness,
+ value,
+ led->pwm->pwm_no[cnt]);
+ }
+ led_cdev->brightness = value;
+}
+
+enum led_brightness (ab8500_led_brightness_get)
+ (struct led_classdev *led_cdev)
+{
+ struct ab8500_led *led = container_of(led_cdev,
+ struct ab8500_led,
+ ab8500_led_cdev);
+ int val;
+
+ val = ab8500_pwm_get_int(led->dev,
+ led_cdev->max_brightness,
+ led->pwm->pwm_no[0]);
+ return val;
+}
+
+static int __devinit ab8500_led_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ int cnt;
+ struct ab8500_led *led, *led_data;
+ struct ab8500_platform_data *plat;
+ struct ab8500_pwm_pdata *cur_led;
+ struct ab8500 *ab8500;
+
+ led_data = kzalloc(sizeof(struct ab8500_led) * TOTAL_NO_PWM,
+ GFP_KERNEL);
+ if (!led_data)
+ return -ENOMEM;
+
+ led = led_data;
+ ab8500 = dev_get_drvdata(pdev->dev.parent);
+ plat = dev_get_platdata(ab8500->dev);
+ if (plat->led == NULL) {
+ kfree(led_data);
+ return -EINVAL;
+ }
+
+ for (cnt = 0; cnt < TOTAL_NO_PWM; cnt++) {
+ cur_led = &plat->led[cnt];
+ if (!cur_led->name)
+ break;
+ led = &led_data[cnt];
+ led->pwm = cur_led;
+ led->dev = &pdev->dev;
+
+ led->ab8500_led_cdev.name = cur_led->name;
+ led->ab8500_led_cdev.max_brightness = cur_led->max_intensity;
+ led->ab8500_led_cdev.brightness_set = ab8500_led_brightness_set;
+ led->ab8500_led_cdev.brightness_get = ab8500_led_brightness_get;
+ ret = led_classdev_register(&pdev->dev, &led->ab8500_led_cdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "led device class register failed\n");
+ while (cnt--) {
+ led = &led_data[cnt];
+ led_classdev_unregister(&led->ab8500_led_cdev);
+ }
+ kfree(led);
+ return ret;
+ }
+ }
+ dev_info(&pdev->dev, "led driver probe success\n");
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int ab8500_led_suspend(struct device *dev)
+{
+ int cnt;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ab8500_led *led = platform_get_drvdata(pdev);
+ struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
+ struct ab8500_platform_data *plat =
+ dev_get_platdata(ab8500->dev);
+ struct ab8500_pwm_pdata *cur_led;
+
+ for (cnt = 0; cnt < TOTAL_NO_PWM; cnt++) {
+ cur_led = &plat->led[cnt];
+ if (!cur_led->name)
+ break;
+ led_classdev_suspend(&led[cnt].ab8500_led_cdev);
+ }
+
+ return 0;
+}
+
+static int ab8500_led_resume(struct device *dev)
+{
+ int cnt;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ab8500_led *led = platform_get_drvdata(pdev);
+ struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
+ struct ab8500_platform_data *plat =
+ dev_get_platdata(ab8500->dev);
+ struct ab8500_pwm_pdata *cur_led;
+
+ for (cnt = 0; cnt < TOTAL_NO_PWM; cnt++) {
+ cur_led = &plat->led[cnt];
+ if (!cur_led->name)
+ break;
+ led_classdev_resume(&led[cnt].ab8500_led_cdev);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops ab8500_led_pm_ops = {
+ .suspend = ab8500_led_suspend,
+ .resume = ab8500_led_resume,
+};
+#endif
+
+static int __devexit ab8500_led_remove(struct platform_device *pdev)
+{
+ struct ab8500_led *led = platform_get_drvdata(pdev);
+ struct ab8500_pwm_pdata *cur_led;
+ struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
+ struct ab8500_platform_data *plat = dev_get_platdata(ab8500->dev);
+ int cnt;
+
+ for (cnt = 0; cnt < TOTAL_NO_PWM; cnt++) {
+ cur_led = &plat->led[cnt];
+ if (!cur_led->name)
+ break;
+ led_classdev_unregister(&led[cnt].ab8500_led_cdev);
+ }
+ kfree(led);
+
+ return 0;
+}
+
+static struct platform_driver ab8500_led_driver = {
+ .driver = {
+ .name = "ab8500-pwm-led",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &ab8500_led_pm_ops,
+#endif
+ },
+ .probe = ab8500_led_probe,
+ .remove = __devexit_p(ab8500_led_remove),
+};
+
+static int __init ab8500_led_init(void)
+{
+ return platform_driver_register(&ab8500_led_driver);
+}
+
+static void __exit ab8500_led_exit(void)
+{
+ platform_driver_unregister(&ab8500_led_driver);
+}
+
+module_init(ab8500_led_init);
+module_exit(ab8500_led_exit);
+
+MODULE_AUTHOR("Dushyanth S R, Arun Murthy");
+MODULE_DESCRIPTION("ab8500-pwm: led driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index b193d45..84a84a8 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -397,6 +397,9 @@ static struct mfd_cell ab8500_devs[] = {
{
.name = "ab8500-pwm-bl",
},
+ {
+ .name = "ab8500-pwm-led",
+ },
{ .name = "ab8500-charger", },
{ .name = "ab8500-audio", },
{ .name = "ab8500-usb", },
diff --git a/include/linux/mfd/ab8500.h b/include/linux/mfd/ab8500.h
index cb8328b..b420dbb 100644
--- a/include/linux/mfd/ab8500.h
+++ b/include/linux/mfd/ab8500.h
@@ -150,6 +150,7 @@ struct ab8500_platform_data {
void (*init) (struct ab8500 *);
struct regulator_init_data *regulator[AB8500_NUM_REGULATORS];
struct ab8500_pwm_pdata *pwm;
+ struct ab8500_pwm_pdata *led;
};

extern int __devinit ab8500_init(struct ab8500 *ab8500);
--
1.6.3.3

2010-08-26 07:14:21

by Arun MURTHY

[permalink] [raw]
Subject: [PATCH 2/3] backlight: u8500: backlight driver based on ab8500 pwm

This patch adds backlight driver for u8500 platform.
Backlight intensity is controlled by Analog Baseband Chip AB8500
Pulse Width Modulation(pwm).

Signed-off-by: Arun Murthy <[email protected]>
Acked-by: Mattias Wallin <[email protected]>
Acked-by: Linus Walleij <[email protected]>
---
drivers/mfd/ab8500-core.c | 4 +-
drivers/video/backlight/Kconfig | 10 ++
drivers/video/backlight/Makefile | 2 +-
drivers/video/backlight/ab8500_bl.c | 209 +++++++++++++++++++++++++++++++++++
include/linux/mfd/ab8500.h | 4 +
5 files changed, 227 insertions(+), 2 deletions(-)
create mode 100644 drivers/video/backlight/ab8500_bl.c

diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index 6548f50..b193d45 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -394,10 +394,12 @@ static struct mfd_cell ab8500_devs[] = {
.num_resources = ARRAY_SIZE(ab8500_rtc_resources),
.resources = ab8500_rtc_resources,
},
+ {
+ .name = "ab8500-pwm-bl",
+ },
{ .name = "ab8500-charger", },
{ .name = "ab8500-audio", },
{ .name = "ab8500-usb", },
- { .name = "ab8500-pwm", },
{ .name = "ab8500-regulator", },
};

diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index e54a337..61bf6c9 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -126,6 +126,16 @@ config BACKLIGHT_CLASS_DEVICE

if BACKLIGHT_CLASS_DEVICE

+config BACKLIGHT_AB8500_LCD
+ bool "ab8500 lcd backlight control"
+ depends on BACKLIGHT_CLASS_DEVICE && AB8500_CORE
+ select AB8500_PWM
+ help
+ This provides backlight control to U8500 platform primary
+ and secondary lcd.
+ If in doubt, it's safe to enable this option; it doesn't kick
+ in unless the board's description says it's wired that way.
+
config BACKLIGHT_ATMEL_LCDC
bool "Atmel LCDC Contrast-as-Backlight control"
depends on FB_ATMEL
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
index 44c0f81..a796418 100644
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -35,4 +35,4 @@ obj-$(CONFIG_BACKLIGHT_ADP5520) += adp5520_bl.o
obj-$(CONFIG_BACKLIGHT_ADP8860) += adp8860_bl.o
obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o
obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o
-
+obj-$(CONFIG_BACKLIGHT_AB8500_LCD) += ab8500_bl.o
diff --git a/drivers/video/backlight/ab8500_bl.c b/drivers/video/backlight/ab8500_bl.c
new file mode 100644
index 0000000..dea3f76
--- /dev/null
+++ b/drivers/video/backlight/ab8500_bl.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Arun R Murthy <[email protected]>
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/backlight.h>
+#include <linux/mfd/ab8500.h>
+#include <linux/mfd/abx500/ab8500-pwm.h>
+
+struct ab8500_bl {
+ struct device *dev;
+ struct ab8500_pwm_pdata *pwm;
+ struct backlight_device *ab8500_bklt;
+ struct backlight_properties ab8500_bklt_props;
+};
+
+static int ab8500_bl_update_status(struct backlight_device *bl)
+{
+ struct ab8500_bl *bklt = bl_get_data(bl);
+ int cnt, ret;
+ int value = bl->props.brightness;
+
+ for (cnt = 0; cnt < MAX_PWM_CTRL; cnt++) {
+ if (!bklt->pwm->pwm_no[cnt])
+ break;
+ if (value > bklt->ab8500_bklt_props.max_brightness)
+ value = bklt->ab8500_bklt_props.max_brightness;
+ ret = ab8500_pwm_set_int(bklt->dev,
+ bklt->ab8500_bklt_props.max_brightness,
+ value, bklt->pwm->pwm_no[cnt]);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static int ab8500_bl_get_brightness(struct backlight_device *bl)
+{
+ struct ab8500_bl *bklt = bl_get_data(bl);
+ int val = 0;
+
+ val = ab8500_pwm_get_int(bklt->dev,
+ bklt->ab8500_bklt_props.max_brightness,
+ bklt->pwm->pwm_no[0]);
+
+ return val;
+}
+
+static const struct backlight_ops ab8500_bklt_fops = {
+ .update_status = ab8500_bl_update_status,
+ .get_brightness = ab8500_bl_get_brightness,
+};
+
+static int __devinit ab8500_bl_probe(struct platform_device *pdev)
+{
+ int cnt;
+ int ret = 0;
+ struct ab8500_bl *bklt, *bl_data;
+ struct ab8500_platform_data *plat;
+ struct ab8500_pwm_pdata *cur_pwm;
+ struct ab8500 *ab8500;
+
+ bl_data = kzalloc(sizeof(struct ab8500_bl) * TOTAL_NO_PWM, GFP_KERNEL);
+ if (!bl_data)
+ return -ENOMEM;
+
+ ab8500 = dev_get_drvdata(pdev->dev.parent);
+ plat = dev_get_platdata(ab8500->dev);
+ if (plat->pwm == NULL) {
+ kfree(bl_data);
+ return -EINVAL;
+ }
+
+ for (cnt = 0; cnt < TOTAL_NO_PWM; cnt++) {
+ cur_pwm = &plat->pwm[cnt];
+ if (!cur_pwm->name)
+ break;
+ bklt = &bl_data[cnt];
+ bklt->pwm = cur_pwm;
+ bklt->dev = &pdev->dev;
+
+ bklt->ab8500_bklt_props.max_brightness = cur_pwm->max_intensity;
+ bklt->ab8500_bklt = backlight_device_register(
+ cur_pwm->name,
+ &pdev->dev, bklt, &ab8500_bklt_fops,
+ &bklt->ab8500_bklt_props);
+ if (IS_ERR(bklt->ab8500_bklt)) {
+ dev_err(&pdev->dev,
+ "failed registering backlight driver\n");
+ while (cnt--) {
+ bklt = &bl_data[cnt];
+ backlight_device_unregister
+ (bklt->ab8500_bklt);
+ }
+ ret = PTR_ERR(bklt->ab8500_bklt);
+ kfree(bklt);
+ return ret;
+ }
+ bklt->ab8500_bklt->props.brightness =
+ bklt->ab8500_bklt_props.max_brightness;
+ backlight_update_status(bklt->ab8500_bklt);
+ }
+ dev_info(&pdev->dev, "backlight probe successful\n");
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int ab8500_bl_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
+ struct ab8500_bl *bklt, *bl_data = platform_get_drvdata(pdev);
+ struct ab8500_pwm_pdata *cur_pwm;
+ struct ab8500_platform_data *plat = dev_get_platdata(ab8500->dev);
+ int cnt, ret;
+
+ for (cnt = 0; cnt < TOTAL_NO_PWM; cnt++) {
+ cur_pwm = &plat->pwm[cnt];
+ if (!cur_pwm->name)
+ break;
+ bklt = &bl_data[cnt];
+ ret = ab8500_pwm_set_int(bklt->dev,
+ bklt->ab8500_bklt_props.max_brightness,
+ 0, bklt->pwm->pwm_no[cnt]);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ab8500_bl_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ab8500_bl *bklt , *bl_data = platform_get_drvdata(pdev);
+ struct ab8500_pwm_pdata *cur_pwm;
+ struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
+ struct ab8500_platform_data *plat = dev_get_platdata(ab8500->dev);
+ int cnt;
+
+ for (cnt = 0; cnt < TOTAL_NO_PWM; cnt++) {
+ cur_pwm = &plat->pwm[cnt];
+ if (!cur_pwm->name)
+ break;
+ bklt = &bl_data[cnt];
+ backlight_update_status(bklt->ab8500_bklt);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops ab8500_bl_pm_ops = {
+ .suspend = ab8500_bl_suspend,
+ .resume = ab8500_bl_resume,
+};
+#endif
+
+static int __devexit ab8500_bl_remove(struct platform_device *pdev)
+{
+ struct ab8500_bl *bklt = platform_get_drvdata(pdev);
+ struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
+ struct ab8500_platform_data *plat = dev_get_platdata(ab8500->dev);
+ struct ab8500_pwm_pdata *cur_pwm;
+ int cnt;
+
+ for (cnt = 0; cnt < TOTAL_NO_PWM; cnt++) {
+ cur_pwm = &plat->pwm[cnt];
+ if (!cur_pwm->name)
+ break;
+ backlight_device_unregister(bklt[cnt].ab8500_bklt);
+ }
+ kfree(bklt);
+
+ return 0;
+}
+
+static struct platform_driver ab8500_bl_driver = {
+ .driver = {
+ .name = "ab8500-pwm-bl",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &ab8500_bl_pm_ops,
+#endif
+ },
+ .probe = ab8500_bl_probe,
+ .remove = __devexit_p(ab8500_bl_remove),
+};
+
+static int __init ab8500_bl_init(void)
+{
+ return platform_driver_register(&ab8500_bl_driver);
+}
+
+static void __exit ab8500_bl_exit(void)
+{
+ platform_driver_unregister(&ab8500_bl_driver);
+}
+
+module_init(ab8500_bl_init);
+module_exit(ab8500_bl_exit);
+
+MODULE_AUTHOR("Dushyanth S R, Arun Murthy");
+MODULE_DESCRIPTION("ab8500-pwm: lcd backlight");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/ab8500.h b/include/linux/mfd/ab8500.h
index d63b605..cb8328b 100644
--- a/include/linux/mfd/ab8500.h
+++ b/include/linux/mfd/ab8500.h
@@ -136,6 +136,9 @@ struct ab8500 {

struct regulator_init_data;

+/* forward declaration */
+struct ab8500_pwm_data;
+
/**
* struct ab8500_platform_data - AB8500 platform data
* @irq_base: start of AB8500 IRQs, AB8500_NR_IRQS will be used
@@ -146,6 +149,7 @@ struct ab8500_platform_data {
int irq_base;
void (*init) (struct ab8500 *);
struct regulator_init_data *regulator[AB8500_NUM_REGULATORS];
+ struct ab8500_pwm_pdata *pwm;
};

extern int __devinit ab8500_init(struct ab8500 *ab8500);
--
1.6.3.3

2010-08-26 07:23:00

by Arun MURTHY

[permalink] [raw]
Subject: [PATCH 1/3] mfd: ab8500-pwm: ab8500 pwm management

This patch exports functions to set and get ab8500 pwm intensity,
which in turn is used by backlight/led driver.

Signed-off-by: Arun Murthy <[email protected]>
Acked-by: Mattias Wallin <[email protected]>
Acked-by: Linus Walleij <[email protected]>
---
drivers/mfd/Kconfig | 8 ++
drivers/mfd/Makefile | 1 +
drivers/mfd/ab8500-pwm.c | 191 +++++++++++++++++++++++++++++++++
include/linux/mfd/abx500/ab8500-pwm.h | 32 ++++++
4 files changed, 232 insertions(+), 0 deletions(-)
create mode 100644 drivers/mfd/ab8500-pwm.c
create mode 100644 include/linux/mfd/abx500/ab8500-pwm.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 82d013f..6e73227 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -515,6 +515,14 @@ config AB3550_CORE
LEDs, vibrator, system power and temperature, power management
and ALSA sound.

+config AB8500_PWM
+ bool
+ depends on AB8500_CORE
+ help
+ This driver exports function to set and get AB8500 Pulse
+ Width Modulation pwm intensity in Analog Baseband chip AB8500.
+ It is used to control the led and lcd backlight intensity.
+
config MFD_TIMBERDALE
tristate "Support for the Timberdale FPGA"
select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 9aa8a2d..949c5bb 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -71,6 +71,7 @@ obj-$(CONFIG_AB3550_CORE) += ab3550-core.o
obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-spi.o
obj-$(CONFIG_AB8500_I2C_CORE) += ab8500-i2c.o
obj-$(CONFIG_AB8500_DEBUG) += ab8500-debugfs.o
+obj-$(CONFIG_AB8500_PWM) += ab8500-pwm.o
obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o
obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
obj-$(CONFIG_LPC_SCH) += lpc_sch.o
diff --git a/drivers/mfd/ab8500-pwm.c b/drivers/mfd/ab8500-pwm.c
new file mode 100644
index 0000000..91eea95
--- /dev/null
+++ b/drivers/mfd/ab8500-pwm.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Arun R Murthy <[email protected]>
+ * License terms: GNU General Public License (GPL) version 2
+ */
+#define KMSG_COMPONENT "ab8500-pwm"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/err.h>
+#include <linux/mfd/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500-pwm.h>
+
+/*
+ * PWM Out generators
+ * Bank: 0x10
+ */
+#define AB8500_PWM_OUT_CTRL1_REG 0x60
+#define AB8500_PWM_OUT_CTRL2_REG 0x61
+#define AB8500_PWM_OUT_CTRL3_REG 0x62
+#define AB8500_PWM_OUT_CTRL4_REG 0x63
+#define AB8500_PWM_OUT_CTRL5_REG 0x64
+#define AB8500_PWM_OUT_CTRL6_REG 0x65
+#define AB8500_PWM_OUT_CTRL7_REG 0x66
+
+/* backlight driver constants */
+#define DISABLE_PWM1 0
+#define DISABLE_PWM2 0
+#define DISABLE_PWM3 0
+#define ENABLE_PWM1 0x01
+#define ENABLE_PWM2 0x02
+#define ENABLE_PWM3 0x04
+
+/**
+ * ab8500_pwm_set_int() - set pwm intensity
+ * @dev: pointer the struct device
+ * @max_brightness: maximum brightness
+ * @intensity: pwm intensity to be set
+ * @pwm: pwm number for which intensity has to be set
+ */
+int ab8500_pwm_set_int(struct device *dev, unsigned int max_brightness,
+ unsigned int intensity, int pwm)
+{
+ int ret = 0;
+ unsigned int higher_val, lower_val, pwm_val;
+
+ /*
+ * DutyPWMOut[9:0]: 0000000000 = 1/1024; 0000000001 = 2/1024;..
+ * 1111111110 = 1023/1024; 1111111111 = 1024/1024.
+ */
+ pwm_val = ((1023 * intensity) / max_brightness);
+ /*
+ * get the first 8 bits that are be written to
+ * AB8500_PWM_OUT_CTRL1_REG[0:7]
+ */
+ lower_val = pwm_val & 0x00FF;
+ /*
+ * get bits [9:10] that are to be written to
+ * AB8500_PWM_OUT_CTRL2_REG[0:1]
+ */
+ higher_val = ((pwm_val & 0x0300) >> 8);
+ switch (pwm) {
+ case 1:
+ /* pwm-1 */
+ ret = abx500_mask_and_set_register_interruptible(dev,
+ AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
+ ENABLE_PWM1, ENABLE_PWM1);
+ if (ret < 0)
+ return ret;
+ ret = abx500_set_register_interruptible(dev,
+ AB8500_MISC, AB8500_PWM_OUT_CTRL1_REG,
+ (u8)lower_val);
+ if (ret < 0)
+ return ret;
+ ret = abx500_set_register_interruptible(dev,
+ AB8500_MISC, AB8500_PWM_OUT_CTRL2_REG,
+ (u8)higher_val);
+ break;
+ case 2:
+ /* pwm-2 */
+ ret = abx500_mask_and_set_register_interruptible(dev,
+ AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
+ ENABLE_PWM2, ENABLE_PWM2);
+ if (ret < 0)
+ return ret;
+ ret = abx500_set_register_interruptible(dev,
+ AB8500_MISC, AB8500_PWM_OUT_CTRL3_REG,
+ (u8)lower_val);
+ if (ret < 0)
+ return ret;
+ ret = abx500_set_register_interruptible(dev,
+ AB8500_MISC, AB8500_PWM_OUT_CTRL4_REG,
+ (u8)higher_val);
+ break;
+ case 3:
+ /* pwm-3 */
+ ret = abx500_mask_and_set_register_interruptible(dev,
+ AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
+ ENABLE_PWM3, ENABLE_PWM3);
+ if (ret < 0)
+ return ret;
+ ret = abx500_set_register_interruptible(dev,
+ AB8500_MISC, AB8500_PWM_OUT_CTRL5_REG,
+ (u8)lower_val);
+ if (ret < 0)
+ return ret;
+ ret = abx500_set_register_interruptible(dev,
+ AB8500_MISC, AB8500_PWM_OUT_CTRL6_REG,
+ (u8)higher_val);
+ break;
+ default:
+ pr_err("%s: invalid pwm entry = %d\n", __func__, pwm);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(ab8500_pwm_set_int);
+
+/**
+ * ab8500_pwm_get_int() - get pwm intensity
+ * @dev: pointer to the struct device
+ * @max_brightness: maximum brightness
+ * @pwm: pwm number for which intensity has to be obtained
+ */
+int ab8500_pwm_get_int(struct device *dev, int max_brightness, int pwm)
+{
+ u8 lower_val, higher_val;
+ unsigned int pwm_val;
+ int ret;
+
+ switch (pwm) {
+ case 1:
+ /* pwm-1 */
+ ret = abx500_get_register_interruptible(dev,
+ AB8500_MISC, AB8500_PWM_OUT_CTRL1_REG,
+ &lower_val);
+ if (ret < 0)
+ return ret;
+ ret = abx500_get_register_interruptible(dev,
+ AB8500_MISC, AB8500_PWM_OUT_CTRL2_REG,
+ &higher_val);
+ if (ret < 0)
+ return ret;
+ break;
+ case 2:
+ /* pwm-2 */
+ ret = abx500_get_register_interruptible(dev,
+ AB8500_MISC, AB8500_PWM_OUT_CTRL3_REG,
+ &lower_val);
+ if (ret < 0)
+ return ret;
+ ret = abx500_get_register_interruptible(dev,
+ AB8500_MISC, AB8500_PWM_OUT_CTRL4_REG,
+ &higher_val);
+ if (ret < 0)
+ return ret;
+ break;
+ case 3:
+ /* pwm-3 */
+ ret = abx500_get_register_interruptible(dev,
+ AB8500_MISC, AB8500_PWM_OUT_CTRL5_REG,
+ &lower_val);
+ if (ret < 0)
+ return ret;
+ ret = abx500_get_register_interruptible(dev,
+ AB8500_MISC, AB8500_PWM_OUT_CTRL6_REG,
+ &higher_val);
+ if (ret < 0)
+ return ret;
+ break;
+ default:
+ pr_err("%s:invalid pwm entry = %d\n", __func__, pwm);
+ return -EINVAL;
+ };
+ /*
+ * PWM value is of 10bit length bits, hence combining the
+ * lower AB8500_PWM_OUT_CTRL1_REG and higher
+ * AB8500_PWM_OUT_CTRL2_REG register.
+ */
+ pwm_val = higher_val << 8 | lower_val;
+ /*
+ * DutyPWMOut[9:0]: 0000000000 = 1/1024; 0000000001 = 2/1024;..
+ * 1111111110 = 1023/1024; 1111111111 = 1024/1024.
+ */
+ pwm_val = (max_brightness * pwm_val) / 1023;
+
+ return pwm_val;
+}
+EXPORT_SYMBOL(ab8500_pwm_get_int);
diff --git a/include/linux/mfd/abx500/ab8500-pwm.h b/include/linux/mfd/abx500/ab8500-pwm.h
new file mode 100644
index 0000000..f891c70
--- /dev/null
+++ b/include/linux/mfd/abx500/ab8500-pwm.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Dushyanth S R <[email protected]>
+ * Author: Arun Murthy <[email protected]>
+ * License Terms: GNU General Public License v2
+ */
+#ifndef MFD_AB8500_PWM_H
+#define MFD_AB8500_PWM_H
+
+#define PWM1 1
+#define PWM2 2
+#define PWM3 3
+#define TOTAL_NO_PWM 3
+#define MAX_PWM_CTRL 2
+
+struct ab8500_pwm_pdata {
+ int pwm_no[MAX_PWM_CTRL];
+ int max_intensity;
+ char *name;
+};
+
+int ab8500_pwm_set_int(struct device *dev,
+ unsigned int max_brightness,
+ unsigned int intensity,
+ int pwm);
+
+int ab8500_pwm_get_int(struct device *dev,
+ int max_brightness,
+ int pwm);
+
+#endif /* MFD_AB8500_PWM_H */
--
1.6.3.3

2010-08-26 10:16:29

by Mike Rapoport

[permalink] [raw]
Subject: Re: [PATCH 0/3] u8500: led and backlight driver

Arun Murthy wrote:
> Arun Murthy (3):
> mfd: ab8500-pwm: ab8500 pwm management
> backlight: u8500: backlight driver based on ab8500 pwm
> leds: ab8500-led: led driver based on ab8500 pwm

There are drivers/leds/leds-pwm.c and drivers/video/backlight/pwm_bl.c that
implement led and backlight drivers based on generic pwm interface.
Have you considered using those?

> drivers/leds/Kconfig | 9 ++
> drivers/leds/Makefile | 1 +
> drivers/leds/leds-ab8500.c | 201 +++++++++++++++++++++++++++++++
> drivers/mfd/Kconfig | 8 ++
> drivers/mfd/Makefile | 1 +
> drivers/mfd/ab8500-core.c | 7 +-
> drivers/mfd/ab8500-pwm.c | 191 ++++++++++++++++++++++++++++++
> drivers/video/backlight/Kconfig | 10 ++
> drivers/video/backlight/Makefile | 2 +-
> drivers/video/backlight/ab8500_bl.c | 209 +++++++++++++++++++++++++++++++++
> include/linux/mfd/ab8500.h | 5 +
> include/linux/mfd/abx500/ab8500-pwm.h | 32 +++++
> 12 files changed, 674 insertions(+), 2 deletions(-)
> create mode 100644 drivers/leds/leds-ab8500.c
> create mode 100644 drivers/mfd/ab8500-pwm.c
> create mode 100644 drivers/video/backlight/ab8500_bl.c
> create mode 100644 include/linux/mfd/abx500/ab8500-pwm.h
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/


--
Sincerely yours,
Mike.