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

From: Arun Murthy
Date: Thu Aug 26 2010 - 03:14:24 EST


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 <arun.murthy@xxxxxxxxxxxxxx>
Acked-by: Mattias Wallin <mattias.wallin@xxxxxxxxxxxxxx>
Acked-by: Linus Walleij <linus.walleij@xxxxxxxxxxxxxx>
---
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 <arun.murthy@xxxxxxxxxxxxxx>
+ * 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

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/