diff options
Diffstat (limited to 'drivers/video/backlight')
-rw-r--r-- | drivers/video/backlight/Kconfig | 41 | ||||
-rw-r--r-- | drivers/video/backlight/Makefile | 6 | ||||
-rw-r--r-- | drivers/video/backlight/mxc_ipu_bl.c | 155 | ||||
-rw-r--r-- | drivers/video/backlight/mxc_lcdc_bl.c | 160 | ||||
-rw-r--r-- | drivers/video/backlight/mxc_mc13892_bl.c | 177 | ||||
-rw-r--r-- | drivers/video/backlight/mxc_pmic_bl.c | 197 | ||||
-rw-r--r-- | drivers/video/backlight/stmp37xx_bl.c | 378 | ||||
-rw-r--r-- | drivers/video/backlight/wm8350_bl.c | 298 |
8 files changed, 1412 insertions, 0 deletions
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index f9d19be05540..0da9bbbe6c92 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -229,3 +229,44 @@ config BACKLIGHT_SAHARA help If you have a Tabletkiosk Sahara Touch-iT, say y to enable the backlight driver. + +menuconfig BACKLIGHT_MXC + bool "Freescale MXC/i.MX Backlight Drivers" + depends on BACKLIGHT_CLASS_DEVICE && ARCH_MXC + default y + help + If you have a Freescale MC13783 PMIC, say y to enable the + backlight driver. + +config BACKLIGHT_MXC_IPU + tristate "IPU PWM Backlight Driver" + depends on BACKLIGHT_MXC && MXC_IPU_V1 + default y + +config BACKLIGHT_MXC_LCDC + tristate "LCDC PWM Backlight Driver" + depends on BACKLIGHT_MXC && (ARCH_MX21 || ARCH_MX27 || ARCH_MX25) + default y + +config BACKLIGHT_MXC_PMIC + tristate "PMIC Backlight Driver" + depends on BACKLIGHT_MXC && MXC_MC13783_LIGHT && MXC_MC13783_POWER + default y + +config BACKLIGHT_MXC_MC13892 + tristate "Mc13892 Backlight Driver" + depends on BACKLIGHT_MXC && MXC_MC13892_LIGHT + default y + +config BACKLIGHT_STMP37XX + tristate "SigmaTel STMP37xx Backlight Driver" + depends on BACKLIGHT_CLASS_DEVICE && (ARCH_STMP37XX || ARCH_STMP378X) + default y + help + If you have a STMP37xx, say y to enable the + backlight driver. + +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 4eb178c1d684..3b3abd75e2d8 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -25,3 +25,9 @@ obj-$(CONFIG_BACKLIGHT_MBP_NVIDIA) += mbp_nvidia_bl.o obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.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 +obj-$(CONFIG_BACKLIGHT_MXC_MC13892) += mxc_mc13892_bl.o +obj-$(CONFIG_BACKLIGHT_STMP37XX) += stmp37xx_bl.o diff --git a/drivers/video/backlight/mxc_ipu_bl.c b/drivers/video/backlight/mxc_ipu_bl.c new file mode 100644 index 000000000000..95b044cdd7e2 --- /dev/null +++ b/drivers/video/backlight/mxc_ipu_bl.c @@ -0,0 +1,155 @@ +/* + * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +/*! + * @defgroup IPU_BL MXC IPU Backlight Driver + */ +/*! + * @file mxc_ipu_bl.c + * + * @brief Backlight Driver for IPU PWM on Freescale MXC/i.MX platforms. + * + * This file contains API defined in include/linux/clk.h for setting up and + * retrieving clocks. + * + * Based on Sharp's Corgi Backlight Driver + * + * @ingroup IPU_BL + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/fb.h> +#include <linux/backlight.h> +#include <linux/ipu.h> + +#define MXC_MAX_INTENSITY 255 +#define MXC_DEFAULT_INTENSITY 127 +#define MXC_INTENSITY_OFF 0 + +struct mxcbl_dev_data { + int intensity; +}; + +static int fb_id; + +static int mxcbl_send_intensity(struct backlight_device *bd) +{ + int intensity = bd->props.brightness; + struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev); + + if (bd->props.power != FB_BLANK_UNBLANK) + intensity = 0; + if (bd->props.fb_blank != FB_BLANK_UNBLANK) + intensity = 0; + + ipu_sdc_set_brightness(intensity); + + devdata->intensity = intensity; + return 0; +} + +static int mxcbl_get_intensity(struct backlight_device *bd) +{ + struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev); + return devdata->intensity; +} + +static int mxcbl_check_fb(struct fb_info *info) +{ + int id = info->fix.id[4] - '0'; + if (id == fb_id) { + if ((id == 3) && !strcmp(info->fix.id, "DISP3 FG")) { + return 0; + } + return 1; + } + return 0; +} + +static struct backlight_ops mxcbl_ops = { + .get_brightness = mxcbl_get_intensity, + .update_status = mxcbl_send_intensity, + .check_fb = mxcbl_check_fb, +}; + +static int __init mxcbl_probe(struct platform_device *pdev) +{ + struct backlight_device *bd; + struct mxcbl_dev_data *devdata; + int ret = 0; + + devdata = kzalloc(sizeof(struct mxcbl_dev_data), GFP_KERNEL); + if (!devdata) + return -ENOMEM; + fb_id = (int)pdev->dev.platform_data; + + bd = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, devdata, + &mxcbl_ops); + if (IS_ERR(bd)) { + ret = PTR_ERR(bd); + goto err0; + } + platform_set_drvdata(pdev, bd); + + bd->props.brightness = MXC_DEFAULT_INTENSITY; + bd->props.max_brightness = MXC_MAX_INTENSITY; + bd->props.power = FB_BLANK_UNBLANK; + bd->props.fb_blank = FB_BLANK_UNBLANK; + backlight_update_status(bd); + + printk("MXC Backlight Device %s Initialized.\n", dev_name(&pdev->dev)); + return 0; + err0: + kfree(devdata); + return ret; +} + +static int mxcbl_remove(struct platform_device *pdev) +{ + struct backlight_device *bd = platform_get_drvdata(pdev); + + bd->props.brightness = MXC_INTENSITY_OFF; + backlight_update_status(bd); + + backlight_device_unregister(bd); + + return 0; +} + +static struct platform_driver mxcbl_driver = { + .probe = mxcbl_probe, + .remove = mxcbl_remove, + .driver = { + .name = "mxc_ipu_bl", + }, +}; + +static int __init mxcbl_init(void) +{ + return platform_driver_register(&mxcbl_driver); +} + +static void __exit mxcbl_exit(void) +{ + platform_driver_unregister(&mxcbl_driver); +} + +late_initcall(mxcbl_init); +module_exit(mxcbl_exit); + +MODULE_DESCRIPTION("Freescale MXC/i.MX IPU PWM Backlight Driver"); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/backlight/mxc_lcdc_bl.c b/drivers/video/backlight/mxc_lcdc_bl.c new file mode 100644 index 000000000000..dce952d11950 --- /dev/null +++ b/drivers/video/backlight/mxc_lcdc_bl.c @@ -0,0 +1,160 @@ +/* + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +/*! + * @defgroup LCDC_BL MXC LCDC Backlight Driver + */ +/*! + * @file mxc_lcdc_bl.c + * + * @brief Backlight Driver for LCDC PWM on Freescale MXC/i.MX platforms. + * + * This file contains API defined in include/linux/clk.h for setting up and + * retrieving clocks. + * + * Based on Sharp's Corgi Backlight Driver + * + * @ingroup LCDC_BL + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/fb.h> +#include <linux/backlight.h> +#include <linux/clk.h> + +#define MXC_MAX_INTENSITY 255 +#define MXC_DEFAULT_INTENSITY 127 +#define MXC_INTENSITY_OFF 0 + +extern void mx2fb_set_brightness(uint8_t); + +struct mxcbl_dev_data { + struct clk *clk; + int intensity; +}; + +static int mxcbl_send_intensity(struct backlight_device *bd) +{ + int intensity = bd->props.brightness; + struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev); + + if (bd->props.power != FB_BLANK_UNBLANK) + intensity = 0; + if (bd->props.fb_blank != FB_BLANK_UNBLANK) + intensity = 0; + + if ((devdata->intensity == 0) && (intensity != 0)) + clk_enable(devdata->clk); + + /* PWM contrast control register */ + mx2fb_set_brightness(intensity); + + if ((devdata->intensity != 0) && (intensity == 0)) + clk_disable(devdata->clk); + + devdata->intensity = intensity; + return 0; +} + +static int mxcbl_get_intensity(struct backlight_device *bd) +{ + struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev); + return devdata->intensity; +} + +static int mxcbl_check_fb(struct fb_info *info) +{ + if (strcmp(info->fix.id, "DISP0 BG") == 0) { + return 1; + } + return 0; +} + +static struct backlight_ops mxcbl_ops = { + .get_brightness = mxcbl_get_intensity, + .update_status = mxcbl_send_intensity, + .check_fb = mxcbl_check_fb, +}; + +static int __init mxcbl_probe(struct platform_device *pdev) +{ + struct backlight_device *bd; + struct mxcbl_dev_data *devdata; + int ret = 0; + + devdata = kzalloc(sizeof(struct mxcbl_dev_data), GFP_KERNEL); + if (!devdata) + return -ENOMEM; + + devdata->clk = clk_get(NULL, "lcdc_clk"); + + bd = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, devdata, + &mxcbl_ops); + if (IS_ERR(bd)) { + ret = PTR_ERR(bd); + goto err0; + } + platform_set_drvdata(pdev, bd); + + bd->props.brightness = MXC_DEFAULT_INTENSITY; + bd->props.max_brightness = MXC_MAX_INTENSITY; + bd->props.power = FB_BLANK_UNBLANK; + bd->props.fb_blank = FB_BLANK_UNBLANK; + mx2fb_set_brightness(MXC_DEFAULT_INTENSITY); + + printk("MXC Backlight Device %s Initialized.\n", dev_name(&pdev->dev)); + return 0; + err0: + kfree(devdata); + return ret; +} + +static int mxcbl_remove(struct platform_device *pdev) +{ + struct backlight_device *bd = platform_get_drvdata(pdev); + + bd->props.brightness = MXC_INTENSITY_OFF; + backlight_update_status(bd); + + backlight_device_unregister(bd); + + return 0; +} + +static struct platform_driver mxcbl_driver = { + .probe = mxcbl_probe, + .remove = mxcbl_remove, + .driver = { + .name = "mxc_lcdc_bl", + }, +}; + +static int __init mxcbl_init(void) +{ + return platform_driver_register(&mxcbl_driver); +} + +static void __exit mxcbl_exit(void) +{ + platform_driver_unregister(&mxcbl_driver); +} + +module_init(mxcbl_init); +module_exit(mxcbl_exit); + +MODULE_DESCRIPTION("Freescale MXC/i.MX LCDC PWM Backlight Driver"); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/backlight/mxc_mc13892_bl.c b/drivers/video/backlight/mxc_mc13892_bl.c new file mode 100644 index 000000000000..6640dd5fce70 --- /dev/null +++ b/drivers/video/backlight/mxc_mc13892_bl.c @@ -0,0 +1,177 @@ +/* + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/fb.h> +#include <linux/backlight.h> + +#include <linux/pmic_light.h> +#include <linux/pmic_external.h> + +/* +#define MXC_MAX_INTENSITY 255 +#define MXC_DEFAULT_INTENSITY 127 +*/ +/* workaround for atlas hot issue */ +#define MXC_MAX_INTENSITY 128 +#define MXC_DEFAULT_INTENSITY 64 + +#define MXC_INTENSITY_OFF 0 + +struct mxcbl_dev_data { + int intensity; + int suspend; +}; + +static int mxcbl_set_intensity(struct backlight_device *bd) +{ + int brightness = bd->props.brightness; + struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev); + + if (bd->props.power != FB_BLANK_UNBLANK) + brightness = 0; + if (bd->props.fb_blank != FB_BLANK_UNBLANK) + brightness = 0; + if (devdata->suspend) + brightness = 0; + + brightness = brightness / 4; + mc13892_bklit_set_dutycycle(LIT_MAIN, brightness); + devdata->intensity = brightness; + + return 0; +} + +static int mxcbl_get_intensity(struct backlight_device *bd) +{ + struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev); + return devdata->intensity; +} + +static int mxcbl_check_fb(struct fb_info *info) +{ + char *id = info->fix.id; + + if (!strcmp(id, "DISP3 BG")) + return 1; + else + return 0; +} + +static struct backlight_ops bl_ops; + +static int __init mxcbl_probe(struct platform_device *pdev) +{ + int ret = 0; + struct backlight_device *bd; + struct mxcbl_dev_data *devdata; + pmic_version_t pmic_version; + + pr_debug("mc13892 backlight start probe\n"); + + devdata = kzalloc(sizeof(struct mxcbl_dev_data), GFP_KERNEL); + if (!devdata) + return -ENOMEM; + + bl_ops.check_fb = mxcbl_check_fb; + bl_ops.get_brightness = mxcbl_get_intensity; + bl_ops.update_status = mxcbl_set_intensity; + bd = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, devdata, + &bl_ops); + if (IS_ERR(bd)) { + ret = PTR_ERR(bd); + goto err0; + } + + platform_set_drvdata(pdev, bd); + + /* according to LCD spec, current should be 18mA */ + /* workaround for MC13892 TO1.1 crash issue, set current 6mA */ + pmic_version = pmic_get_version(); + if (pmic_version.revision < 20) + mc13892_bklit_set_current(LIT_MAIN, LIT_CURR_6); + else + mc13892_bklit_set_current(LIT_MAIN, LIT_CURR_18); + bd->props.brightness = MXC_DEFAULT_INTENSITY; + bd->props.max_brightness = MXC_MAX_INTENSITY; + bd->props.power = FB_BLANK_UNBLANK; + bd->props.fb_blank = FB_BLANK_UNBLANK; + backlight_update_status(bd); + pr_debug("mc13892 backlight probed successfully\n"); + return 0; + + err0: + kfree(devdata); + return ret; +} + +static int mxcbl_remove(struct platform_device *pdev) +{ + struct backlight_device *bd = platform_get_drvdata(pdev); + struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev); + + kfree(devdata); + backlight_device_unregister(bd); + return 0; +} + +static int mxcbl_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct backlight_device *bd = platform_get_drvdata(pdev); + struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev); + + devdata->suspend = 1; + backlight_update_status(bd); + return 0; +} + +static int mxcbl_resume(struct platform_device *pdev) +{ + struct backlight_device *bd = platform_get_drvdata(pdev); + struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev); + + devdata->suspend = 0; + backlight_update_status(bd); + return 0; +} + +static struct platform_driver mxcbl_driver = { + .probe = mxcbl_probe, + .remove = mxcbl_remove, + .suspend = mxcbl_suspend, + .resume = mxcbl_resume, + .driver = { + .name = "mxc_mc13892_bl", + }, +}; + +static int __init mxcbl_init(void) +{ + return platform_driver_register(&mxcbl_driver); +} + +static void __exit mxcbl_exit(void) +{ + platform_driver_unregister(&mxcbl_driver); +} + +module_init(mxcbl_init); +module_exit(mxcbl_exit); + +MODULE_DESCRIPTION("Freescale MXC/i.MX PMIC Backlight Driver"); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/backlight/mxc_pmic_bl.c b/drivers/video/backlight/mxc_pmic_bl.c new file mode 100644 index 000000000000..add55596e445 --- /dev/null +++ b/drivers/video/backlight/mxc_pmic_bl.c @@ -0,0 +1,197 @@ +/* + * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +/*! + * @defgroup PMIC_BL MXC PMIC Backlight Driver + */ +/*! + * @file mxc_pmic_bl.c + * + * @brief PMIC Backlight Driver for Freescale MXC/i.MX platforms. + * + * This file contains API defined in include/linux/clk.h for setting up and + * retrieving clocks. + * + * Based on Sharp's Corgi Backlight Driver + * + * @ingroup PMIC_BL + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/fb.h> +#include <linux/backlight.h> +#include <linux/pmic_light.h> + +#include <mach/pmic_power.h> + +#define MXC_MAX_INTENSITY 255 +#define MXC_DEFAULT_INTENSITY 127 +#define MXC_INTENSITY_OFF 0 + +struct mxcbl_dev_data { + int bl_id; + int intensity; + struct backlight_ops bl_ops; +}; + +static int pmic_bl_use_count; +static int main_fb_id; +static int sec_fb_id; + +static int mxcbl_send_intensity(struct backlight_device *bd) +{ + int intensity = bd->props.brightness; + struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev); + + if (bd->props.power != FB_BLANK_UNBLANK) + intensity = 0; + if (bd->props.fb_blank != FB_BLANK_UNBLANK) + intensity = 0; + + intensity = intensity / 16; + pmic_bklit_set_dutycycle(devdata->bl_id, intensity); + + devdata->intensity = intensity; + return 0; +} + +static int mxcbl_get_intensity(struct backlight_device *bd) +{ + struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev); + return devdata->intensity; +} + +static int mxcbl_check_main_fb(struct fb_info *info) +{ + int id = info->fix.id[4] - '0'; + + if (id == main_fb_id) { + return 1; + } else { + return 0; + } +} + +static int mxcbl_check_sec_fb(struct fb_info *info) +{ + int id = info->fix.id[4] - '0'; + + if (id == sec_fb_id) { + return 1; + } else { + return 0; + } +} + +static int __init mxcbl_probe(struct platform_device *pdev) +{ + int ret = 0; + struct backlight_device *bd; + struct mxcbl_dev_data *devdata; + + devdata = kzalloc(sizeof(struct mxcbl_dev_data), GFP_KERNEL); + if (!devdata) + return -ENOMEM; + devdata->bl_id = pdev->id; + + if (pdev->id == 0) { + devdata->bl_ops.check_fb = mxcbl_check_main_fb; + main_fb_id = (int)pdev->dev.platform_data; + } else { + devdata->bl_ops.check_fb = mxcbl_check_sec_fb; + sec_fb_id = (int)pdev->dev.platform_data; + } + + devdata->bl_ops.get_brightness = mxcbl_get_intensity; + devdata->bl_ops.update_status = mxcbl_send_intensity, + bd = + backlight_device_register(dev_name(&pdev->dev), &pdev->dev, devdata, + &devdata->bl_ops); + if (IS_ERR(bd)) { + ret = PTR_ERR(bd); + goto err0; + } + + platform_set_drvdata(pdev, bd); + + if (pmic_bl_use_count++ == 0) { + pmic_power_regulator_on(SW_SW3); + pmic_power_regulator_set_lp_mode(SW_SW3, LOW_POWER_CTRL_BY_PIN); + + pmic_bklit_tcled_master_enable(); + pmic_bklit_enable_edge_slow(); + pmic_bklit_set_cycle_time(0); + } + + pmic_bklit_set_current(devdata->bl_id, 7); + bd->props.brightness = MXC_DEFAULT_INTENSITY; + bd->props.max_brightness = MXC_MAX_INTENSITY; + bd->props.power = FB_BLANK_UNBLANK; + bd->props.fb_blank = FB_BLANK_UNBLANK; + backlight_update_status(bd); + + printk("MXC Backlight Device %s Initialized.\n", dev_name(&pdev->dev)); + return 0; + err0: + kfree(devdata); + return ret; +} + +static int mxcbl_remove(struct platform_device *pdev) +{ + struct backlight_device *bd = platform_get_drvdata(pdev); + + bd->props.brightness = MXC_INTENSITY_OFF; + backlight_update_status(bd); + + if (--pmic_bl_use_count == 0) { + pmic_bklit_tcled_master_disable(); + + pmic_power_regulator_off(SW_SW3); + pmic_power_regulator_set_lp_mode(SW_SW3, LOW_POWER_CTRL_BY_PIN); + } + + backlight_device_unregister(bd); + + printk("MXC Backlight Driver Unloaded\n"); + + return 0; +} + +static struct platform_driver mxcbl_driver = { + .probe = mxcbl_probe, + .remove = mxcbl_remove, + .driver = { + .name = "mxc_pmic_bl", + }, +}; + +static int __init mxcbl_init(void) +{ + return platform_driver_register(&mxcbl_driver); +} + +static void __exit mxcbl_exit(void) +{ + platform_driver_unregister(&mxcbl_driver); +} + +module_init(mxcbl_init); +module_exit(mxcbl_exit); + +MODULE_DESCRIPTION("Freescale MXC/i.MX PMIC Backlight Driver"); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/backlight/stmp37xx_bl.c b/drivers/video/backlight/stmp37xx_bl.c new file mode 100644 index 000000000000..6b4d9a0ccbaa --- /dev/null +++ b/drivers/video/backlight/stmp37xx_bl.c @@ -0,0 +1,378 @@ +/* + * Backlight Driver for Freescale STMP37XX/STMP378X + * + * Embedded Alley Solutions, Inc <source@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/fb.h> +#include <linux/backlight.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/regulator/consumer.h> + +#include <mach/lcdif.h> +#include <mach/regulator.h> + +struct stmp3xxx_bl_data { + struct notifier_block nb; + struct notifier_block reg_nb; + struct notifier_block reg_init_nb; + struct backlight_device *bd; + struct stmp3xxx_platform_bl_data *pdata; + int current_intensity; + int saved_intensity; + int stmp3xxxbl_suspended; + int stmp3xxxbl_constrained; +}; + +static int stmp3xxxbl_do_probe(struct stmp3xxx_bl_data *data, + struct stmp3xxx_platform_bl_data *pdata); +static int stmp3xxxbl_set_intensity(struct backlight_device *bd); +static inline void bl_register_reg(struct stmp3xxx_platform_bl_data *pdata, + struct stmp3xxx_bl_data *data); + + +/* + * If we got here init is done + */ +static int bl_init_reg_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct stmp3xxx_bl_data *bdata; + struct stmp3xxx_platform_bl_data *pdata; + struct regulator *r = regulator_get(NULL, "stmp3xxx-bl-1"); + + bdata = container_of(self, struct stmp3xxx_bl_data, reg_init_nb); + pdata = bdata->pdata; + + if (r && !IS_ERR(r)) + regulator_put(r); + else + goto out; + + bl_register_reg(pdata, bdata); + + if (pdata->regulator) { + + printk(KERN_NOTICE"%s: setting intensity\n", __func__); + + bus_unregister_notifier(&platform_bus_type, + &bdata->reg_init_nb); + mutex_lock(&bdata->bd->ops_lock); + stmp3xxxbl_set_intensity(bdata->bd); + mutex_unlock(&bdata->bd->ops_lock); + } + +out: + return 0; +} + +static int bl_reg_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct stmp3xxx_bl_data *bdata; + struct stmp3xxx_platform_bl_data *pdata; + bdata = container_of(self, struct stmp3xxx_bl_data, reg_nb); + pdata = bdata->pdata; + + mutex_lock(&bdata->bd->ops_lock); + + switch (event) { + case STMP3XXX_REG5V_IS_USB: + bdata->bd->props.max_brightness = pdata->bl_cons_intensity; + bdata->bd->props.brightness = pdata->bl_cons_intensity; + bdata->saved_intensity = bdata->current_intensity; + bdata->stmp3xxxbl_constrained = 1; + break; + case STMP3XXX_REG5V_NOT_USB: + bdata->bd->props.max_brightness = pdata->bl_max_intensity; + bdata->bd->props.brightness = bdata->saved_intensity; + bdata->stmp3xxxbl_constrained = 0; + break; + } + + stmp3xxxbl_set_intensity(bdata->bd); + mutex_unlock(&bdata->bd->ops_lock); + return 0; +} + +static inline void bl_unregister_reg(struct stmp3xxx_platform_bl_data *pdata, + struct stmp3xxx_bl_data *data) +{ + if (!pdata) + return; + if (pdata->regulator) + regulator_unregister_notifier(pdata->regulator, + &data->reg_nb); + if (pdata->regulator) + regulator_put(pdata->regulator); + pdata->regulator = NULL; +} + +static inline void bl_register_reg(struct stmp3xxx_platform_bl_data *pdata, + struct stmp3xxx_bl_data *data) +{ + pdata->regulator = regulator_get(NULL, "stmp3xxx-bl-1"); + if (pdata->regulator && !IS_ERR(pdata->regulator)) { + regulator_set_mode(pdata->regulator, REGULATOR_MODE_FAST); + if (pdata->regulator) { + data->reg_nb.notifier_call = bl_reg_callback; + regulator_register_notifier(pdata->regulator, + &data->reg_nb); + } + } else{ + printk(KERN_ERR "%s: failed to get regulator\n", __func__); + pdata->regulator = NULL; + } + +} + +static int bl_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct stmp3xxx_platform_fb_entry *pentry = data; + struct stmp3xxx_bl_data *bdata; + struct stmp3xxx_platform_bl_data *pdata; + + switch (event) { + case STMP3XXX_LCDIF_PANEL_INIT: + bdata = container_of(self, struct stmp3xxx_bl_data, nb); + pdata = pentry->bl_data; + bdata->pdata = pdata; + if (pdata) { + bl_register_reg(pdata, bdata); + if (!pdata->regulator) { + /* wait for regulator to appear */ + bdata->reg_init_nb.notifier_call = + bl_init_reg_callback; + bus_register_notifier(&platform_bus_type, + &bdata->reg_init_nb); + } + return stmp3xxxbl_do_probe(bdata, pdata); + } + break; + + case STMP3XXX_LCDIF_PANEL_RELEASE: + bdata = container_of(self, struct stmp3xxx_bl_data, nb); + pdata = pentry->bl_data; + if (pdata) { + bus_unregister_notifier(&platform_bus_type, + &bdata->reg_init_nb); + bl_unregister_reg(pdata, bdata); + pdata->free_bl(pdata); + } + bdata->pdata = NULL; + break; + } + return 0; +} + +#ifdef CONFIG_PM +static int stmp3xxxbl_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct stmp3xxx_bl_data *data = platform_get_drvdata(pdev); + struct stmp3xxx_platform_bl_data *pdata = data->pdata; + + data->stmp3xxxbl_suspended = 1; + if (pdata) { + dev_dbg(&pdev->dev, "real suspend\n"); + stmp3xxxbl_set_intensity(data->bd); + } + return 0; +} + +static int stmp3xxxbl_resume(struct platform_device *pdev) +{ + struct stmp3xxx_bl_data *data = platform_get_drvdata(pdev); + struct stmp3xxx_platform_bl_data *pdata = data->pdata; + int ret = 0; + + data->stmp3xxxbl_suspended = 0; + if (pdata) { + dev_dbg(&pdev->dev, "real resume\n"); + pdata->free_bl(pdata); + ret = pdata->init_bl(pdata); + if (ret) + goto out; + stmp3xxxbl_set_intensity(data->bd); + } +out: + return ret; +} +#else +#define stmp3xxxbl_suspend NULL +#define stmp3xxxbl_resume NULL +#endif +/* + * This function should be called with bd->ops_lock held + * Suspend/resume ? + */ +static int stmp3xxxbl_set_intensity(struct backlight_device *bd) +{ + struct platform_device *pdev = dev_get_drvdata(&bd->dev); + struct stmp3xxx_bl_data *data = platform_get_drvdata(pdev); + struct stmp3xxx_platform_bl_data *pdata = data->pdata; + + if (pdata) { + int ret; + + ret = pdata->set_bl_intensity(pdata, bd, + data->stmp3xxxbl_suspended); + if (ret) + bd->props.brightness = data->current_intensity; + else + data->current_intensity = bd->props.brightness; + return ret; + } else + return -ENODEV; +} + +static int stmp3xxxbl_get_intensity(struct backlight_device *bd) +{ + struct platform_device *pdev = dev_get_drvdata(&bd->dev); + struct stmp3xxx_bl_data *data = platform_get_drvdata(pdev); + + return data->current_intensity; +} + +static struct backlight_ops stmp3xxxbl_ops = { + .get_brightness = stmp3xxxbl_get_intensity, + .update_status = stmp3xxxbl_set_intensity, +}; + +static int stmp3xxxbl_do_probe(struct stmp3xxx_bl_data *data, + struct stmp3xxx_platform_bl_data *pdata) +{ + int ret = pdata->init_bl(pdata); + + if (ret) + goto out; + + data->bd->props.power = FB_BLANK_UNBLANK; + data->bd->props.fb_blank = FB_BLANK_UNBLANK; + if (data->stmp3xxxbl_constrained) { + data->bd->props.max_brightness = pdata->bl_cons_intensity; + data->bd->props.brightness = pdata->bl_cons_intensity; + } else { + data->bd->props.max_brightness = pdata->bl_max_intensity; + data->bd->props.brightness = pdata->bl_default_intensity; + } + + data->pdata = pdata; + stmp3xxxbl_set_intensity(data->bd); + +out: + return ret; +} + +static int __init stmp3xxxbl_probe(struct platform_device *pdev) +{ + struct stmp3xxx_bl_data *data; + struct stmp3xxx_platform_bl_data *pdata = pdev->dev.platform_data; + int ret = 0; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto out; + } + data->bd = backlight_device_register(pdev->name, &pdev->dev, pdev, + &stmp3xxxbl_ops); + if (IS_ERR(data->bd)) { + ret = PTR_ERR(data->bd); + goto out_1; + } + + get_device(&pdev->dev); + + data->nb.notifier_call = bl_callback; + stmp3xxx_lcdif_register_client(&data->nb); + platform_set_drvdata(pdev, data); + + if (pdata) { + ret = stmp3xxxbl_do_probe(data, pdata); + if (ret) + goto out_2; + } + + goto out; + +out_2: + put_device(&pdev->dev); +out_1: + kfree(data); +out: + return ret; +} + +static int stmp3xxxbl_remove(struct platform_device *pdev) +{ + struct stmp3xxx_platform_bl_data *pdata = pdev->dev.platform_data; + struct stmp3xxx_bl_data *data = platform_get_drvdata(pdev); + struct backlight_device *bd = data->bd; + + bd->props.power = FB_BLANK_POWERDOWN; + bd->props.fb_blank = FB_BLANK_POWERDOWN; + bd->props.brightness = 0; + data->current_intensity = bd->props.brightness; + + if (pdata) { + pdata->set_bl_intensity(pdata, bd, data->stmp3xxxbl_suspended); + if (pdata->free_bl) + pdata->free_bl(pdata); + } + backlight_device_unregister(bd); + if (pdata->regulator) + regulator_put(pdata->regulator); + put_device(&pdev->dev); + platform_set_drvdata(pdev, NULL); + stmp3xxx_lcdif_unregister_client(&data->nb); + kfree(data); + + return 0; +} + +static struct platform_driver stmp3xxxbl_driver = { + .probe = stmp3xxxbl_probe, + .remove = __devexit_p(stmp3xxxbl_remove), + .suspend = stmp3xxxbl_suspend, + .resume = stmp3xxxbl_resume, + .driver = { + .name = "stmp3xxx-bl", + .owner = THIS_MODULE, + }, +}; + +static int __init stmp3xxx_init(void) +{ + return platform_driver_register(&stmp3xxxbl_driver); +} + +static void __exit stmp3xxx_exit(void) +{ + platform_driver_unregister(&stmp3xxxbl_driver); +} + +module_init(stmp3xxx_init); +module_exit(stmp3xxx_exit); + +MODULE_AUTHOR("Embedded Alley Solutions, Inc <sources@embeddedalley.com>"); +MODULE_DESCRIPTION("STMP3xxx Backlight Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/backlight/wm8350_bl.c b/drivers/video/backlight/wm8350_bl.c new file mode 100644 index 000000000000..88014fba6b7e --- /dev/null +++ b/drivers/video/backlight/wm8350_bl.c @@ -0,0 +1,298 @@ +/* + * 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/consumer.h> +#include <linux/mfd/wm8350/pmic.h> +#include <linux/mfd/wm8350/bl.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_limit(isink, + 0, 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_limit(isink, 0, 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; + 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); + return PTR_ERR(dcdc); + } + + bl = kzalloc(sizeof(*bl), GFP_KERNEL); + if (bl == NULL) { + regulator_put(isink); + regulator_put(dcdc); + 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); + + wm8350_bl_ops.check_fb = pdata->check_fb; + + bl->device = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, + bl, &wm8350_bl_ops); + if (IS_ERR(bl->device)) { + ret = PTR_ERR(bl->device); + regulator_put(dcdc); + regulator_put(isink); + kfree(bl); + return ret; + } + + bl->notifier.notifier_call = wm8350_bl_notifier; + regulator_register_notifier(dcdc, &bl->notifier); + regulator_register_notifier(isink, &bl->notifier); + bl->device->props = bl->props; + + regulator_set_current_limit(isink, 0, 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_limit(isink, 0, 0); + regulator_disable(isink); + regulator_unregister_notifier(isink, &bl->notifier); + regulator_unregister_notifier(dcdc, &bl->notifier); + regulator_put(isink); + regulator_put(dcdc); + 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"); |