From: "Ying-Chun Liu (PaulLiu)" paul.liu@linaro.org
Add DA9052 video backlight driver from Dialog. Modify Kconfig/Makefile for DA9052 video backlight driver.
Signed-off-by: Zhou Jingyu Jingyu.Zhou@freescale.com Acked-by: Lily Zhang r58066@freescale.com Signed-off-by: Ying-Chun Liu (PaulLiu) paul.liu@linaro.org --- drivers/video/backlight/Kconfig | 6 + drivers/video/backlight/Makefile | 2 +- drivers/video/backlight/da9052_bl.c | 461 +++++++++++++++++++++++++++++++++++ 3 files changed, 468 insertions(+), 1 deletions(-) create mode 100644 drivers/video/backlight/da9052_bl.c
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 0c9373b..a233749 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -315,6 +315,12 @@ config BACKLIGHT_PCF50633 If you have a backlight driven by a NXP PCF50633 MFD, say Y here to enable its driver.
+config BACKLIGHT_DA9052 + tristate "Dialog DA9052 WLED" + depends on PMIC_DIALOG + help + Enable the DA9052 Backlight Driver + endif # BACKLIGHT_CLASS_DEVICE
endif # BACKLIGHT_LCD_SUPPORT diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index b9ca849..0c8334f 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -36,4 +36,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_DA9052) += da9052_bl.o diff --git a/drivers/video/backlight/da9052_bl.c b/drivers/video/backlight/da9052_bl.c new file mode 100644 index 0000000..0692194 --- /dev/null +++ b/drivers/video/backlight/da9052_bl.c @@ -0,0 +1,461 @@ +/* + * Copyright(c) 2009 Dialog Semiconductor Ltd. + * + * 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. + * + * da9052_bl.c: Backlight driver for DA9052 + */ + +#include <linux/platform_device.h> +#include <linux/fb.h> +#include <linux/backlight.h> + +#include <linux/delay.h> + +#include <linux/mfd/da9052/da9052.h> +#include <linux/mfd/da9052/reg.h> +#include <linux/mfd/da9052/bl.h> + + +#define DRIVER_NAME "da9052-backlight" +#define DRIVER_NAME1 "WLED-1" +#define DRIVER_NAME2 "WLED-2" +#define DRIVER_NAME3 "WLED-3" + +/* These flags define if Backlight LEDs are present */ +/* Set the following macros to 1, if LEDs are present. Otherwise set to 0 */ +#define DA9052_LED1_PRESENT 1 +#define DA9052_LED2_PRESENT 1 +#define DA9052_LED3_PRESENT 1 + +#define DA9052_MAX_BRIGHTNESS 0xFF + +struct da9052_backlight_data { + struct device *da9052_dev; + int current_brightness; + struct da9052 *da9052; + + int is_led1_present; + int is_led2_present; + int is_led3_present; +}; + +enum da9052_led_number { + LED1 = 1, + LED2, + LED3, +}; + +static int da9052_backlight_brightness_set(struct da9052_backlight_data *data, + int brightness, enum da9052_led_number led) +{ + /* + * Mechanism for brightness control: + * For brightness control, current is used. + * PWM feature is not used. + * To use PWM feature, a fixed value of current should be defined. + */ + + int ret = 0; + unsigned int led_ramp_bit; + unsigned int led_current_register; + unsigned int led_current_sink_bit; + unsigned int led_boost_en_bit; + struct da9052_ssc_msg msg; + + switch (led) { + case LED1: + led_ramp_bit = DA9052_LEDCONT_LED1RAMP; + led_current_register = DA9052_LED1CONF_REG; + led_current_sink_bit = DA9052_LEDCONT_LED1EN; + led_boost_en_bit = DA9052_BOOST_LED1INEN; + break; + case LED2: + led_ramp_bit = DA9052_LEDCONT_LED2RAMP; + led_current_register = DA9052_LED2CONF_REG; + led_current_sink_bit = DA9052_LEDCONT_LED2EN; + led_boost_en_bit = DA9052_BOOST_LED2INEN; + break; + case LED3: + led_ramp_bit = DA9052_LEDCONT_LED3RAMP; + led_current_register = DA9052_LED3CONF_REG; + led_current_sink_bit = DA9052_LEDCONT_LED3EN; + led_boost_en_bit = DA9052_BOOST_LED3INEN; + break; + default: + return -EIO; + } + + /* + 1. Configure the boost register + 2. Configure the LED _CONT register + 3. Configure the LEDx_CONF registers to the brightness value. + */ + msg.addr = DA9052_BOOST_REG; + msg.data = 0x3F; + if (brightness) { + da9052_lock(data->da9052); + ret = data->da9052->write(data->da9052, &msg); + if (ret) { + da9052_unlock(data->da9052); + return ret; + } + da9052_unlock(data->da9052); + } + + msg.addr = DA9052_LEDCONT_REG; + msg.data = 0xFF; + if (brightness) { + da9052_lock(data->da9052); + ret = data->da9052->write(data->da9052, &msg); + if (ret) { + da9052_unlock(data->da9052); + return ret; + } + da9052_unlock(data->da9052); + } + + msg.addr = led_current_register; + msg.data = 0; + /* Write to the DA9052 register */ + da9052_lock(data->da9052); + ret = data->da9052->write(data->da9052, &msg); + if (ret) { + da9052_unlock(data->da9052); + return ret; + } + da9052_unlock(data->da9052); + msleep(20); + msg.data = brightness; + /* Write to the DA9052 register */ + da9052_lock(data->da9052); + ret = data->da9052->write(data->da9052, &msg); + if (ret) { + da9052_unlock(data->da9052); + return ret; + } + da9052_unlock(data->da9052); + + return 0; +} + +static int da9052_backlight_set(struct backlight_device *bl, int brightness) +{ + struct da9052_backlight_data *data = bl_get_data(bl); + int ret = 0; + /* Check for LED1 */ + if (1 == data->is_led1_present) { + ret = da9052_backlight_brightness_set(data, brightness, LED1); + if (ret) + return ret; + } + /* Check for LED2 */ + if (1 == data->is_led2_present) { + ret = da9052_backlight_brightness_set(data, brightness, LED2); + if (ret) + return ret; + } + /* Check for LED3 */ + if (1 == data->is_led3_present) { + ret = da9052_backlight_brightness_set(data, brightness, LED3); + if (ret) + return ret; + } + + data->current_brightness = brightness; + return 0; +} + +static int da9052_init_WLED(struct da9052_backlight_data *data, + enum da9052_led_number led) +{ + int ret = 0; + unsigned int led_current_register; + struct da9052_ssc_msg msg; + + switch (led) { + case LED1: + led_current_register = DA9052_LED1CONF_REG; + break; + case LED2: + led_current_register = DA9052_LED2CONF_REG; + break; + case LED3: + led_current_register = DA9052_LED3CONF_REG; + break; + default: + return -EIO; + } + + msg.addr = DA9052_BOOST_REG; + msg.data = 0x00; + da9052_lock(data->da9052); + ret = data->da9052->write(data->da9052, &msg); + if (ret) { + da9052_unlock(data->da9052); + return ret; + } + da9052_unlock(data->da9052); + + msg.addr = DA9052_LEDCONT_REG; + msg.data = 0x00; + da9052_lock(data->da9052); + ret = data->da9052->write(data->da9052, &msg); + if (ret) { + da9052_unlock(data->da9052); + return ret; + } + da9052_unlock(data->da9052); + + msg.addr = led_current_register; + msg.data = 0; + da9052_lock(data->da9052); + ret = data->da9052->write(data->da9052, &msg); + da9052_unlock(data->da9052); + return ret; +} + +static int da9052_backlight_update_status(struct backlight_device *bl) +{ + int brightness = bl->props.brightness; + + if (bl->props.power != FB_BLANK_UNBLANK) + brightness = 0; + + if (bl->props.fb_blank != FB_BLANK_UNBLANK) + brightness = 0; + return da9052_backlight_set(bl, brightness); +} + +static int da9052_backlight_get_brightness(struct backlight_device *bl) +{ + struct da9052_backlight_data *data = bl_get_data(bl); + return data->current_brightness; +} + +const struct backlight_ops da9052_backlight_ops = { + .update_status = da9052_backlight_update_status, + .get_brightness = da9052_backlight_get_brightness, +}; + +static int da9052_backlight_probe1(struct platform_device *pdev) +{ + struct da9052_backlight_data *data; + struct backlight_device *bl; + struct backlight_properties props; + int ret = 0; + struct da9052 *da9052 = dev_get_drvdata(pdev->dev.parent); + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + data->da9052_dev = pdev->dev.parent; + data->da9052 = da9052; + data->current_brightness = 0; + data->is_led1_present = DA9052_LED1_PRESENT; + + /* Init the WLED-1 bank */ + ret = da9052_init_WLED(data, LED1); + if (ret) + return ret; + + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = DA9052_MAX_BRIGHTNESS; + bl = backlight_device_register(pdev->name, data->da9052_dev, + data, &da9052_backlight_ops, &props); + if (IS_ERR(bl)) { + dev_err(&pdev->dev, "failed to register backlight\n"); + kfree(data); + return PTR_ERR(bl); + } + + bl->props.brightness = 0; + + platform_set_drvdata(pdev, bl); + backlight_update_status(bl); + + return 0; +} +static int da9052_backlight_probe2(struct platform_device *pdev) +{ + struct da9052_backlight_data *data; + struct backlight_device *bl; + struct da9052 *da9052 = dev_get_drvdata(pdev->dev.parent); + struct backlight_properties props; + int ret = 0; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + data->da9052_dev = pdev->dev.parent; + data->da9052 = da9052; + data->current_brightness = 0; + data->is_led2_present = DA9052_LED2_PRESENT; + + /* Init the WLED-2 bank */ + ret = da9052_init_WLED(data, LED2); + if (ret) + return ret; + + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = DA9052_MAX_BRIGHTNESS; + bl = backlight_device_register(pdev->name, data->da9052_dev, + data, &da9052_backlight_ops, &props); + if (IS_ERR(bl)) { + dev_err(&pdev->dev, "failed to register backlight\n"); + kfree(data); + return PTR_ERR(bl); + } + + bl->props.brightness = 0; + + platform_set_drvdata(pdev, bl); + backlight_update_status(bl); + + return 0; +} +static int da9052_backlight_probe3(struct platform_device *pdev) +{ + struct da9052_backlight_data *data; + struct backlight_device *bl; + struct da9052 *da9052 = dev_get_drvdata(pdev->dev.parent); + struct backlight_properties props; + int ret = 0; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + data->da9052_dev = pdev->dev.parent; + data->da9052 = da9052; + data->current_brightness = 0; + data->is_led3_present = DA9052_LED3_PRESENT; + + /* Init the WLED-2 bank */ + ret = da9052_init_WLED(data, LED3); + if (ret) + return ret; + + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = DA9052_MAX_BRIGHTNESS; + bl = backlight_device_register(pdev->name, data->da9052_dev, + data, &da9052_backlight_ops, &props); + if (IS_ERR(bl)) { + dev_err(&pdev->dev, "failed to register backlight\n"); + kfree(data); + return PTR_ERR(bl); + } + + bl->props.brightness = 0; + + platform_set_drvdata(pdev, bl); + + backlight_update_status(bl); + return 0; +} + +static int da9052_backlight_remove1(struct platform_device *pdev) +{ + struct backlight_device *bl = platform_get_drvdata(pdev); + struct da9052_backlight_data *data = bl_get_data(bl); + int ret = 0; + + /* Switch off the WLED-1 */ + ret = da9052_init_WLED(data, LED1); + if (ret) + return ret; + + backlight_device_unregister(bl); + kfree(data); + return 0; +} + +static int da9052_backlight_remove2(struct platform_device *pdev) +{ + struct backlight_device *bl = platform_get_drvdata(pdev); + struct da9052_backlight_data *data = bl_get_data(bl); + int ret = 0; + + /* Switch off the WLED-2 */ + ret = da9052_init_WLED(data, LED2); + if (ret) + return ret; + + backlight_device_unregister(bl); + kfree(data); + return 0; +} +static int da9052_backlight_remove3(struct platform_device *pdev) +{ + struct backlight_device *bl = platform_get_drvdata(pdev); + struct da9052_backlight_data *data = bl_get_data(bl); + int ret; + + /* Switch off the WLED-3 */ + ret = da9052_init_WLED(data, LED3); + if (ret) + return ret; + + backlight_device_unregister(bl); + kfree(data); + return 0; +} + +static struct platform_driver da9052_backlight_driver1 = { + .driver = { + .name = DRIVER_NAME1, + .owner = THIS_MODULE, + }, + .probe = da9052_backlight_probe1, + .remove = da9052_backlight_remove1, +}; +static struct platform_driver da9052_backlight_driver2 = { + .driver = { + .name = DRIVER_NAME2, + .owner = THIS_MODULE, + }, + .probe = da9052_backlight_probe2, + .remove = da9052_backlight_remove2, +}; +static struct platform_driver da9052_backlight_driver3 = { + .driver = { + .name = DRIVER_NAME3, + .owner = THIS_MODULE, + }, + .probe = da9052_backlight_probe3, + .remove = da9052_backlight_remove3, +}; + +static int __init da9052_backlight_init(void) +{ + s32 ret; + ret = platform_driver_register(&da9052_backlight_driver1); + if (ret) + return ret; + ret = platform_driver_register(&da9052_backlight_driver2); + if (ret) + return ret; + + ret = platform_driver_register(&da9052_backlight_driver3); + if (ret) + return ret; + + return ret; +} +module_init(da9052_backlight_init); + +static void __exit da9052_backlight_exit(void) +{ + platform_driver_unregister(&da9052_backlight_driver1); + platform_driver_unregister(&da9052_backlight_driver2); + platform_driver_unregister(&da9052_backlight_driver3); +} +module_exit(da9052_backlight_exit); + +MODULE_AUTHOR("Dialog Semiconductor Ltd dchen@diasemi.com"); +MODULE_DESCRIPTION("Backlight driver for Dialog DA9052 PMIC"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRIVER_NAME); +