summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/configs/imx37_3stack_defconfig8
-rw-r--r--arch/arm/mach-mx37/mx37_3stack_pmic_wm8350.c40
-rw-r--r--drivers/regulator/wm8350/reg-wm8350.c4
-rw-r--r--drivers/video/backlight/Kconfig5
-rw-r--r--drivers/video/backlight/Makefile1
-rw-r--r--drivers/video/backlight/wm8350_bl.c301
-rw-r--r--include/linux/regulator/wm8350/wm8350-pmic.h3
7 files changed, 357 insertions, 5 deletions
diff --git a/arch/arm/configs/imx37_3stack_defconfig b/arch/arm/configs/imx37_3stack_defconfig
index 426a8d6e95dc..da7e7269bdb5 100644
--- a/arch/arm/configs/imx37_3stack_defconfig
+++ b/arch/arm/configs/imx37_3stack_defconfig
@@ -781,7 +781,13 @@ CONFIG_SSB_POSSIBLE=y
# CONFIG_VGASTATE is not set
# CONFIG_VIDEO_OUTPUT_CONTROL is not set
# CONFIG_FB is not set
-# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=y
+# CONFIG_LCD_LTV350QV is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_CORGI is not set
+CONFIG_BACKLIGHT_MXC=y
+CONFIG_BACKLIGHT_WM8350=y
#
# Display device support
diff --git a/arch/arm/mach-mx37/mx37_3stack_pmic_wm8350.c b/arch/arm/mach-mx37/mx37_3stack_pmic_wm8350.c
index 0b625a4a59c0..839a2b710de9 100644
--- a/arch/arm/mach-mx37/mx37_3stack_pmic_wm8350.c
+++ b/arch/arm/mach-mx37/mx37_3stack_pmic_wm8350.c
@@ -209,7 +209,7 @@ static int config_hibernate(struct wm8350 *wm8350)
struct regulation_constraints led_regulation_constraints = {
.min_uA = 0,
- .max_uA = 19727,
+ .max_uA = 230000,
.valid_ops_mask = REGULATOR_CHANGE_CURRENT,
};
@@ -227,6 +227,43 @@ static void set_regulator_constraints(struct wm8350 *wm8350)
&led_regulation_constraints);
}
+static void wm8350_nop_release(struct device *dev)
+{
+ /* Nothing */
+}
+
+struct wm8350_bl_platform_data wm8350_bl_data = {
+ .isink = WM8350_ISINK_A,
+ .dcdc = WM8350_DCDC_5,
+ .voltage_ramp = WM8350_DC5_RMP_20V,
+ .retries = 5,
+ .max_brightness = 63,
+ .power = FB_BLANK_UNBLANK,
+ .brightness = 50,
+};
+
+static struct platform_device mxc_wm8350_devices[] = {
+ {
+ .name = "wm8350-bl",
+ .id = 2,
+ .dev = {
+ .release = wm8350_nop_release,
+ .platform_data = &wm8350_bl_data,
+ },
+ },
+};
+
+static inline void mxc_init_wm8350(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mxc_wm8350_devices); i++) {
+ if (platform_device_register(&mxc_wm8350_devices[i]) < 0)
+ dev_err(&mxc_wm8350_devices[i].dev,
+ "Unable to register WM8350 device\n");
+ }
+}
+
int wm8350_init(struct wm8350 *wm8350)
{
int ret = 0;
@@ -239,6 +276,7 @@ int wm8350_init(struct wm8350 *wm8350)
wm8350_device_register_wdg(wm8350);
wm8350_device_register_power(wm8350);
#endif
+ mxc_init_wm8350();
/* register sound */
printk("Registering imx37_snd_device");
diff --git a/drivers/regulator/wm8350/reg-wm8350.c b/drivers/regulator/wm8350/reg-wm8350.c
index 32059a01a352..ffbec586610e 100644
--- a/drivers/regulator/wm8350/reg-wm8350.c
+++ b/drivers/regulator/wm8350/reg-wm8350.c
@@ -74,13 +74,13 @@ static int wm8350_isink_set_current(struct regulator *reg, int uA)
val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_A) &
~WM8350_CS1_ISEL_MASK;
wm8350_reg_write(wm8350, WM8350_CURRENT_SINK_DRIVER_A, val |
- get_isink_val(uA));
+ WM8350_CS1_ENABLE | get_isink_val(uA));
break;
case WM8350_ISINK_B:
val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_B) &
~WM8350_CS1_ISEL_MASK;
wm8350_reg_write(wm8350, WM8350_CURRENT_SINK_DRIVER_B, val |
- get_isink_val(uA));
+ WM8350_CS2_ENABLE | get_isink_val(uA));
break;
default:
return -EINVAL;
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index d00e50da4427..cf9036782dbb 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -113,3 +113,8 @@ config BACKLIGHT_MXC_PMIC
tristate "PMIC Backlight Driver"
depends on BACKLIGHT_MXC && MXC_MC13783_LIGHT && MXC_MC13783_POWER
default y
+
+config BACKLIGHT_WM8350
+ tristate "WM8350 Backlight Driver"
+ depends on BACKLIGHT_MXC && REGULATOR_WM8350
+ default y
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
index 9280ca44c38d..1dda3908eff6 100644
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o
obj-$(CONFIG_BACKLIGHT_MXC_LCDC) += mxc_lcdc_bl.o
obj-$(CONFIG_BACKLIGHT_MXC_IPU) += mxc_ipu_bl.o
obj-$(CONFIG_BACKLIGHT_MXC_PMIC) += mxc_pmic_bl.o
+obj-$(CONFIG_BACKLIGHT_WM8350) += wm8350_bl.o
diff --git a/drivers/video/backlight/wm8350_bl.c b/drivers/video/backlight/wm8350_bl.c
new file mode 100644
index 000000000000..152aea7f9f24
--- /dev/null
+++ b/drivers/video/backlight/wm8350_bl.c
@@ -0,0 +1,301 @@
+/*
+ * Backlight driver for DCDC2 on i.MX32ADS board
+ *
+ * Copyright(C) 2007 Wolfson Microelectronics PLC.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/fb.h>
+#include <linux/platform_device.h>
+#include <linux/backlight.h>
+#include <linux/regulator/regulator.h>
+#include <linux/regulator/wm8350/wm8350-bus.h>
+
+struct wm8350_backlight {
+ struct backlight_properties props;
+ struct backlight_device *device;
+ struct regulator *dcdc;
+ struct regulator *isink;
+ struct notifier_block notifier;
+ struct work_struct work;
+ struct mutex mutex;
+ int intensity;
+ int suspend;
+ int retries;
+};
+
+/* hundredths of uA, 405 = 4.05 uA */
+static const int intensity_huA[] = {
+ 405, 482, 573, 681, 810, 963, 1146, 1362, 1620, 1927, 2291, 2725,
+ 3240, 3853, 4582, 5449, 6480, 7706, 9164, 10898, 12960, 15412, 18328,
+ 21796, 25920, 30824, 36656, 43592, 51840, 61648, 73313, 87184,
+ 103680, 123297, 146626, 174368, 207360, 246594, 293251, 348737,
+ 414720, 493188, 586503, 697473, 829440, 986376, 1173005, 1394946,
+ 1658880, 1972752, 2346011, 2789892, 3317760, 3945504, 4692021,
+ 5579785, 6635520, 7891008, 9384042, 11159570, 13271040, 15782015,
+ 18768085, 22319140,
+};
+
+static void bl_work(struct work_struct *work)
+{
+ struct wm8350_backlight *bl =
+ container_of(work, struct wm8350_backlight, work);
+ struct regulator *isink = bl->isink;
+
+ mutex_lock(&bl->mutex);
+ if (bl->intensity >= 0 &&
+ bl->intensity < ARRAY_SIZE(intensity_huA)) {
+ bl->retries = 0;
+ regulator_set_current(isink,
+ intensity_huA[bl->intensity] / 100);
+ } else
+ printk(KERN_ERR "wm8350: Backlight intensity error\n");
+ mutex_unlock(&bl->mutex);
+}
+
+static int wm8350_bl_notifier(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ struct wm8350_backlight *bl =
+ container_of(self, struct wm8350_backlight, notifier);
+ struct regulator *isink = bl->isink;
+
+ if (event & REGULATOR_EVENT_UNDER_VOLTAGE)
+ printk(KERN_ERR "wm8350: BL DCDC undervoltage\n");
+ if (event & REGULATOR_EVENT_REGULATION_OUT)
+ printk(KERN_ERR "wm8350: BL ISINK out of regulation\n");
+
+ mutex_lock(&bl->mutex);
+ if (bl->retries) {
+ bl->retries--;
+ regulator_disable(isink);
+ regulator_set_current(isink, bl->intensity);
+ regulator_enable(isink);
+ } else {
+ printk(KERN_ERR
+ "wm8350: BL regulation retry failure - disable\n");
+ bl->intensity = 0;
+ regulator_disable(isink);
+ }
+ mutex_unlock(&bl->mutex);
+ return 0;
+}
+
+static int wm8350_bl_send_intensity(struct backlight_device *bd)
+{
+ struct wm8350_backlight *bl =
+ (struct wm8350_backlight *)dev_get_drvdata(&bd->dev);
+ int intensity = bd->props.brightness;
+
+ if (bd->props.power != FB_BLANK_UNBLANK)
+ intensity = 0;
+ if (bd->props.fb_blank != FB_BLANK_UNBLANK)
+ intensity = 0;
+ if (bl->suspend)
+ intensity = 0;
+
+ mutex_lock(&bl->mutex);
+ bl->intensity = intensity;
+ mutex_unlock(&bl->mutex);
+ schedule_work(&bl->work);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm8350_bl_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct wm8350_backlight *bl =
+ (struct wm8350_backlight *)platform_get_drvdata(pdev);
+
+ bl->suspend = 1;
+ backlight_update_status(bl->device);
+ return 0;
+}
+
+static int wm8350_bl_resume(struct platform_device *pdev)
+{
+ struct wm8350_backlight *bl =
+ (struct wm8350_backlight *)platform_get_drvdata(pdev);
+
+ bl->suspend = 0;
+ backlight_update_status(bl->device);
+ return 0;
+}
+#else
+#define wm8350_bl_suspend NULL
+#define wm8350_bl_resume NULL
+#endif
+
+static int wm8350_bl_get_intensity(struct backlight_device *bd)
+{
+ struct wm8350_backlight *bl =
+ (struct wm8350_backlight *)dev_get_drvdata(&bd->dev);
+ return bl->intensity;
+}
+
+static struct backlight_ops wm8350_bl_ops = {
+ .get_brightness = wm8350_bl_get_intensity,
+ .update_status = wm8350_bl_send_intensity,
+};
+
+static int wm8350_bl_probe(struct platform_device *pdev)
+{
+ struct regulator *isink, *dcdc;
+ struct wm8350_backlight *bl;
+ struct wm8350_bl_platform_data *pdata = pdev->dev.platform_data;
+ struct wm8350_pmic *pmic;
+ int ret;
+
+ if (pdata == NULL) {
+ printk(KERN_ERR "%s: no platform data\n", __func__);
+ return -ENODEV;
+ }
+
+ if (pdata->isink != WM8350_ISINK_A && pdata->isink != WM8350_ISINK_B) {
+ printk(KERN_ERR "%s: invalid ISINK\n", __func__);
+ return -EINVAL;
+ }
+ if (pdata->dcdc != WM8350_DCDC_2 && pdata->dcdc != WM8350_DCDC_5) {
+ printk(KERN_ERR "%s: invalid DCDC\n", __func__);
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "wm8350: backlight using %s and %s\n",
+ pdata->isink == WM8350_ISINK_A ? "ISINKA" : "ISINKB",
+ pdata->dcdc == WM8350_DCDC_2 ? "DCDC2" : "DCDC5");
+
+ isink = regulator_get(&pdev->dev,
+ pdata->isink == WM8350_ISINK_A ? "ISINKA" : "ISINKB");
+ if (IS_ERR(isink) || isink == NULL) {
+ printk(KERN_ERR "%s: cant get ISINK\n", __func__);
+ return PTR_ERR(isink);
+ }
+
+ dcdc = regulator_get(&pdev->dev,
+ pdata->dcdc == WM8350_DCDC_2 ? "DCDC2" : "DCDC5");
+ if (IS_ERR(dcdc) || dcdc == NULL) {
+ printk(KERN_ERR "%s: cant get DCDC\n", __func__);
+ regulator_put(isink, &pdev->dev);
+ return PTR_ERR(dcdc);
+ }
+
+ bl = kzalloc(sizeof(*bl), GFP_KERNEL);
+ if (bl == NULL) {
+ regulator_put(isink, &pdev->dev);
+ regulator_put(dcdc, &pdev->dev);
+ return -ENOMEM;
+ }
+
+ mutex_init(&bl->mutex);
+ INIT_WORK(&bl->work, bl_work);
+ bl->props.max_brightness = pdata->max_brightness;
+ bl->props.power = pdata->power;
+ bl->props.brightness = pdata->brightness;
+ bl->retries = pdata->retries;
+ bl->dcdc = dcdc;
+ bl->isink = isink;
+ platform_set_drvdata(pdev, bl);
+ pmic = regulator_get_drvdata(bl->isink);
+
+ bl->device = backlight_device_register(pdev->dev.bus_id, &pdev->dev,
+ bl, &wm8350_bl_ops);
+ if (IS_ERR(bl->device)) {
+ ret = PTR_ERR(bl->device);
+ regulator_put(dcdc, &pdev->dev);
+ regulator_put(isink, &pdev->dev);
+ kfree(bl);
+ return ret;
+ }
+
+ bl->notifier.notifier_call = wm8350_bl_notifier;
+ regulator_register_client(dcdc, &bl->notifier);
+ regulator_register_client(isink, &bl->notifier);
+ bl->device->props = bl->props;
+
+ /* WM8350 ISINK & DCDC setup */
+ if (pdata->isink == WM8350_ISINK_A)
+ pmic->isink_A_dcdc = pdata->dcdc;
+ else
+ pmic->isink_B_dcdc = pdata->dcdc;
+
+ regulator_set_current(isink, 20000);
+
+ wm8350_isink_set_flash(pmic, pdata->isink,
+ WM8350_ISINK_FLASH_DISABLE,
+ WM8350_ISINK_FLASH_TRIG_BIT,
+ WM8350_ISINK_FLASH_DUR_32MS,
+ WM8350_ISINK_FLASH_ON_1_00S,
+ WM8350_ISINK_FLASH_OFF_1_00S,
+ WM8350_ISINK_FLASH_MODE_EN);
+
+ wm8350_dcdc25_set_mode(pmic, pdata->dcdc,
+ WM8350_ISINK_MODE_BOOST, WM8350_ISINK_ILIM_NORMAL,
+ pdata->voltage_ramp, pdata->isink == WM8350_ISINK_A ?
+ WM8350_DC5_FBSRC_ISINKA : WM8350_DC5_FBSRC_ISINKB);
+
+ wm8350_dcdc_set_slot(pmic, pdata->dcdc, 15, 0,
+ pdata->dcdc == WM8350_DCDC_2 ?
+ WM8350_DC2_ERRACT_SHUTDOWN_CONV : WM8350_DC5_ERRACT_NONE);
+
+ regulator_enable(isink);
+ backlight_update_status(bl->device);
+ return 0;
+}
+
+static int wm8350_bl_remove(struct platform_device *pdev)
+{
+ struct wm8350_backlight *bl =
+ (struct wm8350_backlight *)platform_get_drvdata(pdev);
+ struct regulator *isink = bl->isink, *dcdc = bl->dcdc;
+
+ bl->intensity = 0;
+ backlight_update_status(bl->device);
+ schedule_work(&bl->work);
+ flush_scheduled_work();
+ backlight_device_unregister(bl->device);
+
+ regulator_set_current(isink, 0);
+ regulator_disable(isink);
+ regulator_unregister_client(isink, &bl->notifier);
+ regulator_unregister_client(dcdc, &bl->notifier);
+ regulator_put(isink, &pdev->dev);
+ regulator_put(dcdc, &pdev->dev);
+ return 0;
+}
+
+struct platform_driver imx32ads_backlight_driver = {
+ .driver = {
+ .name = "wm8350-bl",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8350_bl_probe,
+ .remove = wm8350_bl_remove,
+ .suspend = wm8350_bl_suspend,
+ .resume = wm8350_bl_resume,
+};
+
+static int __devinit imx32ads_backlight_init(void)
+{
+ return platform_driver_register(&imx32ads_backlight_driver);
+}
+
+static void imx32ads_backlight_exit(void)
+{
+ platform_driver_unregister(&imx32ads_backlight_driver);
+}
+
+device_initcall_sync(imx32ads_backlight_init);
+module_exit(imx32ads_backlight_exit);
+
+MODULE_AUTHOR("Liam Girdwood <lg@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM8350 Backlight driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/regulator/wm8350/wm8350-pmic.h b/include/linux/regulator/wm8350/wm8350-pmic.h
index 7e767eff260b..7054c75b46bb 100644
--- a/include/linux/regulator/wm8350/wm8350-pmic.h
+++ b/include/linux/regulator/wm8350/wm8350-pmic.h
@@ -84,6 +84,7 @@
#define WM8350_CS1_ISEL_SHIFT 0 /* CS1_ISEL - [5:0] */
/* Bit values for R172 (0xAC) */
+#define WM8350_CS1_ENABLE 0x8000 /* CS1_ENA */
#define WM8350_CS1_HIB_MODE_DISABLE 0 /* Disable current sink in hibernate */
#define WM8350_CS1_HIB_MODE_LEAVE 1 /* Leave current sink as-is in hibernate */
@@ -102,7 +103,7 @@
/*
* R174 (0xAE) - Current Sink Driver B
*/
-// lg #define WM8350_CS2_ENA 0x8000 /* CS2_ENA */
+#define WM8350_CS2_ENABLE 0x8000 /* CS2_ENA */
#define WM8350_CS2_HIB_MODE 0x1000 /* CS2_HIB_MODE */
#define WM8350_CS2_ISEL_MASK 0x003F /* CS2_ISEL - [5:0] */