12ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen/*
22ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen *  Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
32ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen *      PCF50633 backlight device driver
42ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen *
52ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen *  This program is free software; you can redistribute	 it and/or modify it
62ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen *  under  the terms of	 the GNU General  Public License as published by the
72ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen *  Free Software Foundation;  either version 2 of the	License, or (at your
82ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen *  option) any later version.
92ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen *
102ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen *  You should have received a copy of the  GNU General Public License along
112ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen *  with this program; if not, write  to the Free Software Foundation, Inc.,
122ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen *  675 Mass Ave, Cambridge, MA 02139, USA.
132ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen *
142ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen */
152ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
162ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen#include <linux/kernel.h>
172ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen#include <linux/module.h>
182ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen#include <linux/slab.h>
192ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen#include <linux/platform_device.h>
202ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
212ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen#include <linux/backlight.h>
222ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen#include <linux/fb.h>
232ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
242ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen#include <linux/mfd/pcf50633/core.h>
252ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen#include <linux/mfd/pcf50633/backlight.h>
262ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
272ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausenstruct pcf50633_bl {
282ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	struct pcf50633 *pcf;
292ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	struct backlight_device *bl;
302ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
312ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	unsigned int brightness;
322ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	unsigned int brightness_limit;
332ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen};
342ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
352ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen/*
362ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen * pcf50633_bl_set_brightness_limit
372ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen *
382ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen * Update the brightness limit for the pc50633 backlight. The actual brightness
392ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen * will not go above the limit. This is useful to limit power drain for example
402ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen * on low battery.
412ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen *
422ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen * @dev: Pointer to a pcf50633 device
432ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen * @limit: The brightness limit. Valid values are 0-63
442ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen */
452ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausenint pcf50633_bl_set_brightness_limit(struct pcf50633 *pcf, unsigned int limit)
462ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen{
472ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	struct pcf50633_bl *pcf_bl = platform_get_drvdata(pcf->bl_pdev);
482ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
492ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	if (!pcf_bl)
502ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen		return -ENODEV;
512ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
522ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	pcf_bl->brightness_limit = limit & 0x3f;
532ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	backlight_update_status(pcf_bl->bl);
542ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
552ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen    return 0;
562ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen}
572ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
582ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausenstatic int pcf50633_bl_update_status(struct backlight_device *bl)
592ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen{
602ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	struct pcf50633_bl *pcf_bl = bl_get_data(bl);
612ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	unsigned int new_brightness;
622ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
632ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
642ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	if (bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK) ||
652ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen		bl->props.power != FB_BLANK_UNBLANK)
662ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen		new_brightness = 0;
672ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	else if (bl->props.brightness < pcf_bl->brightness_limit)
682ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen		new_brightness = bl->props.brightness;
692ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	else
702ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen		new_brightness = pcf_bl->brightness_limit;
712ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
722ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
732ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	if (pcf_bl->brightness == new_brightness)
742ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen		return 0;
752ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
762ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	if (new_brightness) {
772ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen		pcf50633_reg_write(pcf_bl->pcf, PCF50633_REG_LEDOUT,
782ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen					new_brightness);
792ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen		if (!pcf_bl->brightness)
802ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen			pcf50633_reg_write(pcf_bl->pcf, PCF50633_REG_LEDENA, 1);
812ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	} else {
822ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen		pcf50633_reg_write(pcf_bl->pcf, PCF50633_REG_LEDENA, 0);
832ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	}
842ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
852ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	pcf_bl->brightness = new_brightness;
862ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
872ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	return 0;
882ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen}
892ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
902ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausenstatic int pcf50633_bl_get_brightness(struct backlight_device *bl)
912ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen{
922ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	struct pcf50633_bl *pcf_bl = bl_get_data(bl);
932ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	return pcf_bl->brightness;
942ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen}
952ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
962ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausenstatic const struct backlight_ops pcf50633_bl_ops = {
972ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	.get_brightness = pcf50633_bl_get_brightness,
982ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	.update_status	= pcf50633_bl_update_status,
992ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	.options	= BL_CORE_SUSPENDRESUME,
1002ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen};
1012ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
1022ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausenstatic int __devinit pcf50633_bl_probe(struct platform_device *pdev)
1032ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen{
1042ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	struct pcf50633_bl *pcf_bl;
1052ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	struct device *parent = pdev->dev.parent;
1062ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	struct pcf50633_platform_data *pcf50633_data = parent->platform_data;
1072ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	struct pcf50633_bl_platform_data *pdata = pcf50633_data->backlight_data;
1082ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	struct backlight_properties bl_props;
1092ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
110ce969228fdb54a7e3d7cc1ed27367fd4b9525d74Julia Lawall	pcf_bl = devm_kzalloc(&pdev->dev, sizeof(*pcf_bl), GFP_KERNEL);
1112ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	if (!pcf_bl)
1122ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen		return -ENOMEM;
1132ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
114bb7ca747f8d6243b3943c5b133048652020f4a50Matthew Garrett	bl_props.type = BACKLIGHT_RAW;
1152ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	bl_props.max_brightness = 0x3f;
1162ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	bl_props.power = FB_BLANK_UNBLANK;
1172ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
1182ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	if (pdata) {
1192ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen		bl_props.brightness = pdata->default_brightness;
1202ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen		pcf_bl->brightness_limit = pdata->default_brightness_limit;
1212ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	} else {
1222ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen		bl_props.brightness = 0x3f;
1232ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen		pcf_bl->brightness_limit = 0x3f;
1242ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	}
1252ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
1262ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	pcf_bl->pcf = dev_to_pcf50633(pdev->dev.parent);
1272ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
1282ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	pcf_bl->bl = backlight_device_register(pdev->name, &pdev->dev, pcf_bl,
1292ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen						&pcf50633_bl_ops, &bl_props);
1302ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
131ce969228fdb54a7e3d7cc1ed27367fd4b9525d74Julia Lawall	if (IS_ERR(pcf_bl->bl))
132ce969228fdb54a7e3d7cc1ed27367fd4b9525d74Julia Lawall		return PTR_ERR(pcf_bl->bl);
1332ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
1342ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	platform_set_drvdata(pdev, pcf_bl);
1352ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
1362ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	pcf50633_reg_write(pcf_bl->pcf, PCF50633_REG_LEDDIM, pdata->ramp_time);
1372ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
1382ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	/* Should be different from bl_props.brightness, so we do not exit
1392ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	 * update_status early the first time it's called */
1402ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	pcf_bl->brightness = pcf_bl->bl->props.brightness + 1;
1412ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
1422ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	backlight_update_status(pcf_bl->bl);
1432ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
1442ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	return 0;
1452ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen}
1462ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
1472ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausenstatic int __devexit pcf50633_bl_remove(struct platform_device *pdev)
1482ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen{
1492ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	struct pcf50633_bl *pcf_bl = platform_get_drvdata(pdev);
1502ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
1512ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	backlight_device_unregister(pcf_bl->bl);
1522ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
1532ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	platform_set_drvdata(pdev, NULL);
1542ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
1552ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	return 0;
1562ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen}
1572ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
1582ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausenstatic struct platform_driver pcf50633_bl_driver = {
1592ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	.probe =	pcf50633_bl_probe,
1602ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	.remove =	__devexit_p(pcf50633_bl_remove),
1612ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	.driver = {
1622ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen		.name = "pcf50633-backlight",
1632ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen	},
1642ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen};
1652ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
16681178e021689bf86c328f144aa0f0e1b50f5e94cAxel Linmodule_platform_driver(pcf50633_bl_driver);
1672ddfd12f3584840f5190897214423061d8a0602fLars-Peter Clausen
1682ddfd12f3584840f5190897214423061d8a0602fLars-Peter ClausenMODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
1692ddfd12f3584840f5190897214423061d8a0602fLars-Peter ClausenMODULE_DESCRIPTION("PCF50633 backlight driver");
1702ddfd12f3584840f5190897214423061d8a0602fLars-Peter ClausenMODULE_LICENSE("GPL");
1712ddfd12f3584840f5190897214423061d8a0602fLars-Peter ClausenMODULE_ALIAS("platform:pcf50633-backlight");
172