summaryrefslogtreecommitdiff
path: root/drivers/leds/leds-mc13892.c
blob: ec7ae4316b4da8a4e0cc3b538b0665d24443ef93 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
 * Copyright 2008-2010 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/slab.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/pmic_light.h>

static void mc13892_led_set(struct led_classdev *led_cdev,
			    enum led_brightness value)
{
	struct platform_device *dev = to_platform_device(led_cdev->dev->parent);
	int led_ch;

	switch (dev->id) {
	case 'r':
		led_ch = LIT_RED;
		break;
	case 'g':
		led_ch = LIT_GREEN;
		break;
	case 'b':
		led_ch = LIT_BLUE;
		break;
	default:
		return;
	}

	/* set current with medium value, in case current is too large */
	mc13892_bklit_set_current(led_ch, LIT_CURR_12);
	/* max duty cycle is 63, brightness needs to be divided by 4 */
	mc13892_bklit_set_dutycycle(led_ch, value / 4);

}

static int mc13892_led_remove(struct platform_device *dev)
{
	struct led_classdev *led_cdev = platform_get_drvdata(dev);

	led_classdev_unregister(led_cdev);
	kfree(led_cdev->name);
	kfree(led_cdev);

	return 0;
}

#define LED_NAME_LEN	16

static int mc13892_led_probe(struct platform_device *dev)
{
	int ret;
	struct led_classdev *led_cdev;
	char *name;

	led_cdev = kzalloc(sizeof(struct led_classdev), GFP_KERNEL);
	if (led_cdev == NULL) {
		dev_err(&dev->dev, "No memory for device\n");
		return -ENOMEM;
	}
	name = kzalloc(LED_NAME_LEN, GFP_KERNEL);
	if (name == NULL) {
		dev_err(&dev->dev, "No memory for device\n");
		ret = -ENOMEM;
		goto exit_err;
	}

	strcpy(name, dev->name);
	ret = strlen(dev->name);
	if (ret > LED_NAME_LEN - 2) {
		dev_err(&dev->dev, "led name is too long\n");
		goto exit_err1;
	}
	name[ret] = dev->id;
	name[ret + 1] = '\0';
	led_cdev->name = name;
	led_cdev->brightness_set = mc13892_led_set;

	ret = led_classdev_register(&dev->dev, led_cdev);
	if (ret < 0) {
		dev_err(&dev->dev, "led_classdev_register failed\n");
		goto exit_err1;
	}

	platform_set_drvdata(dev, led_cdev);

	return 0;
      exit_err1:
	kfree(led_cdev->name);
      exit_err:
	kfree(led_cdev);
	return ret;
}

#ifdef CONFIG_PM
static int mc13892_led_suspend(struct platform_device *dev, pm_message_t state)
{
	struct led_classdev *led_cdev = platform_get_drvdata(dev);

	led_classdev_suspend(led_cdev);
	return 0;
}

static int mc13892_led_resume(struct platform_device *dev)
{
	struct led_classdev *led_cdev = platform_get_drvdata(dev);

	led_classdev_resume(led_cdev);
	return 0;
}
#else
#define mc13892_led_suspend NULL
#define mc13892_led_resume NULL
#endif

static struct platform_driver mc13892_led_driver = {
	.probe = mc13892_led_probe,
	.remove = mc13892_led_remove,
	.suspend = mc13892_led_suspend,
	.resume = mc13892_led_resume,
	.driver = {
		   .name = "pmic_leds",
		   .owner = THIS_MODULE,
		   },
};

static int __init mc13892_led_init(void)
{
	return platform_driver_register(&mc13892_led_driver);
}

static void __exit mc13892_led_exit(void)
{
	platform_driver_unregister(&mc13892_led_driver);
}

module_init(mc13892_led_init);
module_exit(mc13892_led_exit);

MODULE_DESCRIPTION("Led driver for PMIC mc13892");
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_LICENSE("GPL");