2 * Copyright (C) 2010 Motorola, Inc.
4 * This program is free dispware; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free dispware Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free dispware
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 #include <linux/err.h>
20 #include <linux/leds.h>
21 #include <linux/leds-ld-cpcap.h>
22 #include <linux/platform_device.h>
23 #include <linux/slab.h>
24 #include <linux/workqueue.h>
25 #include <linux/regulator/consumer.h>
26 #include <linux/spi/cpcap.h>
27 #include <linux/spi/cpcap-regbits.h>
29 struct cpcap_led_data {
30 struct led_classdev cpcap_class_dev;
31 struct cpcap_device *cpcap;
32 struct cpcap_led *pdata;
33 struct regulator *regulator;
34 struct work_struct brightness_work;
35 enum led_brightness brightness;
40 static void cpcap_set(struct led_classdev *led_cdev,
41 enum led_brightness brightness)
43 struct cpcap_led_data *cpcap_led_data =
44 container_of(led_cdev, struct cpcap_led_data,
50 cpcap_led_data->brightness = brightness;
51 schedule_work(&cpcap_led_data->brightness_work);
53 EXPORT_SYMBOL(cpcap_set);
55 static int cpcap_led_blink(struct led_classdev *led_cdev,
56 unsigned long *delay_on,
57 unsigned long *delay_off)
59 struct cpcap_led_data *info =
60 container_of(led_cdev, struct cpcap_led_data,
63 info->blink_val = *delay_on;
65 if (info->pdata->blink_able) {
67 cpcap_uc_start(info->cpcap, CPCAP_MACRO_6);
69 cpcap_uc_stop(info->cpcap, CPCAP_MACRO_6);
70 schedule_work(&info->brightness_work);
77 static void cpcap_brightness_work(struct work_struct *work)
80 unsigned short brightness = 0;
82 struct cpcap_led_data *cpcap_led_data =
83 container_of(work, struct cpcap_led_data, brightness_work);
85 brightness = cpcap_led_data->brightness;
88 brightness = (cpcap_led_data->pdata->cpcap_reg_period |
89 cpcap_led_data->pdata->cpcap_reg_duty_cycle |
90 cpcap_led_data->pdata->cpcap_reg_current |
93 if ((cpcap_led_data->regulator) &&
94 (cpcap_led_data->regulator_state == 0)) {
95 regulator_enable(cpcap_led_data->regulator);
96 cpcap_led_data->regulator_state = 1;
99 cpcap_status = cpcap_regacc_write(cpcap_led_data->cpcap,
100 cpcap_led_data->pdata->cpcap_register,
102 cpcap_led_data->pdata->cpcap_reg_mask);
104 if (cpcap_status < 0)
105 pr_err("%s: Writing to the register failed for %i\n",
106 __func__, cpcap_status);
109 if ((cpcap_led_data->regulator) &&
110 (cpcap_led_data->regulator_state == 1)) {
111 regulator_disable(cpcap_led_data->regulator);
112 cpcap_led_data->regulator_state = 0;
114 /* Due to a HW issue turn off the current then
115 turn off the duty cycle */
117 cpcap_status = cpcap_regacc_write(cpcap_led_data->cpcap,
118 cpcap_led_data->pdata->cpcap_register,
120 cpcap_led_data->pdata->cpcap_reg_mask);
124 cpcap_status = cpcap_regacc_write(cpcap_led_data->cpcap,
125 cpcap_led_data->pdata->cpcap_register,
127 cpcap_led_data->pdata->cpcap_reg_mask);
130 if (cpcap_status < 0)
131 pr_err("%s: Writing to the register failed for %i\n",
132 __func__, cpcap_status);
137 static int cpcap_probe(struct platform_device *pdev)
140 struct cpcap_led_data *info;
143 pr_err("%s: platform data required\n", __func__);
147 info = kzalloc(sizeof(struct cpcap_led_data), GFP_KERNEL);
153 info->pdata = pdev->dev.platform_data;
154 info->cpcap = platform_get_drvdata(pdev);
155 platform_set_drvdata(pdev, info);
157 if (info->pdata->led_regulator != NULL) {
158 info->regulator = regulator_get(&pdev->dev,
159 info->pdata->led_regulator);
160 if (IS_ERR(info->regulator)) {
161 pr_err("%s: Cannot get %s regulator\n",
162 __func__, info->pdata->led_regulator);
163 ret = PTR_ERR(info->regulator);
164 goto exit_request_reg_failed;
168 info->regulator_state = 0;
170 info->cpcap_class_dev.name = info->pdata->class_name;
171 info->cpcap_class_dev.brightness_set = cpcap_set;
172 info->cpcap_class_dev.blink_set = cpcap_led_blink;
173 info->cpcap_class_dev.brightness = LED_OFF;
174 info->cpcap_class_dev.max_brightness = 255;
175 if (info->pdata->blink_able)
176 info->cpcap_class_dev.default_trigger = "timer";
178 ret = led_classdev_register(&pdev->dev, &info->cpcap_class_dev);
180 pr_err("%s:Register %s class failed\n",
181 __func__, info->cpcap_class_dev.name);
182 goto err_reg_button_class_failed;
185 INIT_WORK(&info->brightness_work, cpcap_brightness_work);
189 err_reg_button_class_failed:
191 regulator_put(info->regulator);
192 exit_request_reg_failed:
197 static int cpcap_remove(struct platform_device *pdev)
199 struct cpcap_led_data *info = platform_get_drvdata(pdev);
202 regulator_put(info->regulator);
203 led_classdev_unregister(&info->cpcap_class_dev);
207 static struct platform_driver ld_cpcap_driver = {
208 .probe = cpcap_probe,
209 .remove = cpcap_remove,
211 .name = LD_CPCAP_LED_DRV,
215 static int __init led_cpcap_init(void)
217 return platform_driver_register(&ld_cpcap_driver);
220 static void __exit led_cpcap_exit(void)
222 platform_driver_unregister(&ld_cpcap_driver);
225 module_init(led_cpcap_init);
226 module_exit(led_cpcap_exit);
228 MODULE_DESCRIPTION("CPCAP Lighting driver");
229 MODULE_AUTHOR("Dan Murphy <D.Murphy@Motorola.com>");
230 MODULE_LICENSE("GPL");