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 queue_work(system_nrt_wq, &cpcap_led_data->brightness_work);
53 EXPORT_SYMBOL(cpcap_set);
56 cpcap_led_set_blink(struct cpcap_led_data *info, unsigned long blink)
58 info->blink_val = blink;
60 if (info->pdata->blink_able) {
62 cpcap_uc_start(info->cpcap, CPCAP_MACRO_6);
64 cpcap_uc_stop(info->cpcap, CPCAP_MACRO_6);
65 queue_work(system_nrt_wq, &info->brightness_work);
70 static int cpcap_led_blink(struct led_classdev *led_cdev,
71 unsigned long *delay_on,
72 unsigned long *delay_off)
74 struct cpcap_led_data *info =
75 container_of(led_cdev, struct cpcap_led_data,
78 cpcap_led_set_blink(info, *delay_on);
82 static void cpcap_brightness_work(struct work_struct *work)
85 unsigned short brightness = 0;
87 struct cpcap_led_data *cpcap_led_data =
88 container_of(work, struct cpcap_led_data, brightness_work);
90 brightness = cpcap_led_data->brightness;
93 brightness = (cpcap_led_data->pdata->cpcap_reg_period |
94 cpcap_led_data->pdata->cpcap_reg_duty_cycle |
95 cpcap_led_data->pdata->cpcap_reg_current |
98 if ((cpcap_led_data->regulator) &&
99 (cpcap_led_data->regulator_state == 0)) {
100 regulator_enable(cpcap_led_data->regulator);
101 cpcap_led_data->regulator_state = 1;
104 cpcap_status = cpcap_regacc_write(cpcap_led_data->cpcap,
105 cpcap_led_data->pdata->cpcap_register,
107 cpcap_led_data->pdata->cpcap_reg_mask);
109 if (cpcap_status < 0)
110 pr_err("%s: Writing to the register failed for %i\n",
111 __func__, cpcap_status);
114 if ((cpcap_led_data->regulator) &&
115 (cpcap_led_data->regulator_state == 1)) {
116 regulator_disable(cpcap_led_data->regulator);
117 cpcap_led_data->regulator_state = 0;
119 /* Due to a HW issue turn off the current then
120 turn off the duty cycle */
122 cpcap_status = cpcap_regacc_write(cpcap_led_data->cpcap,
123 cpcap_led_data->pdata->cpcap_register,
125 cpcap_led_data->pdata->cpcap_reg_mask);
129 cpcap_status = cpcap_regacc_write(cpcap_led_data->cpcap,
130 cpcap_led_data->pdata->cpcap_register,
132 cpcap_led_data->pdata->cpcap_reg_mask);
135 if (cpcap_status < 0)
136 pr_err("%s: Writing to the register failed for %i\n",
137 __func__, cpcap_status);
142 static ssize_t blink_show(struct device *dev,
143 struct device_attribute *attr, char *buf)
145 struct led_classdev *led_cdev = dev_get_drvdata(dev);
146 struct cpcap_led_data *info =
147 container_of(led_cdev, struct cpcap_led_data, cpcap_class_dev);
149 return sprintf(buf, "%u\n", info->blink_val);
152 static ssize_t blink_store(struct device *dev,
153 struct device_attribute *attr, const char *buf, size_t size)
155 struct led_classdev *led_cdev = dev_get_drvdata(dev);
156 struct cpcap_led_data *info =
157 container_of(led_cdev, struct cpcap_led_data, cpcap_class_dev);
158 unsigned long blink = simple_strtoul(buf, NULL, 10);
159 cpcap_led_set_blink(info, blink);
163 static DEVICE_ATTR(blink, 0644, blink_show, blink_store);
165 static int cpcap_probe(struct platform_device *pdev)
168 struct cpcap_led_data *info;
171 pr_err("%s: platform data required\n", __func__);
175 info = kzalloc(sizeof(struct cpcap_led_data), GFP_KERNEL);
181 info->pdata = pdev->dev.platform_data;
182 info->cpcap = platform_get_drvdata(pdev);
183 platform_set_drvdata(pdev, info);
185 if (info->pdata->led_regulator != NULL) {
186 info->regulator = regulator_get(&pdev->dev,
187 info->pdata->led_regulator);
188 if (IS_ERR(info->regulator)) {
189 pr_err("%s: Cannot get %s regulator\n",
190 __func__, info->pdata->led_regulator);
191 ret = PTR_ERR(info->regulator);
192 goto exit_request_reg_failed;
196 info->regulator_state = 0;
198 info->cpcap_class_dev.name = info->pdata->class_name;
199 info->cpcap_class_dev.brightness_set = cpcap_set;
200 info->cpcap_class_dev.blink_set = cpcap_led_blink;
201 info->cpcap_class_dev.brightness = LED_OFF;
202 info->cpcap_class_dev.max_brightness = 255;
203 if (info->pdata->blink_able)
204 info->cpcap_class_dev.default_trigger = "timer";
206 ret = led_classdev_register(&pdev->dev, &info->cpcap_class_dev);
208 pr_err("%s:Register %s class failed\n",
209 __func__, info->cpcap_class_dev.name);
210 goto err_reg_button_class_failed;
213 /* Create a device file to control blinking.
214 * We do this to avoid problems setting permissions on the
215 * timer trigger delay_on and delay_off files.
217 ret = device_create_file(info->cpcap_class_dev.dev, &dev_attr_blink);
219 pr_err("%s:device_create_file failed for blink\n", __func__);
220 goto err_device_create_file_failed;
223 INIT_WORK(&info->brightness_work, cpcap_brightness_work);
227 err_device_create_file_failed:
228 led_classdev_unregister(&info->cpcap_class_dev);
229 err_reg_button_class_failed:
231 regulator_put(info->regulator);
232 exit_request_reg_failed:
237 static int cpcap_remove(struct platform_device *pdev)
239 struct cpcap_led_data *info = platform_get_drvdata(pdev);
241 device_remove_file(info->cpcap_class_dev.dev, &dev_attr_blink);
243 regulator_put(info->regulator);
244 led_classdev_unregister(&info->cpcap_class_dev);
248 static struct platform_driver ld_cpcap_driver = {
249 .probe = cpcap_probe,
250 .remove = cpcap_remove,
252 .name = LD_CPCAP_LED_DRV,
256 static int __init led_cpcap_init(void)
258 return platform_driver_register(&ld_cpcap_driver);
261 static void __exit led_cpcap_exit(void)
263 platform_driver_unregister(&ld_cpcap_driver);
266 module_init(led_cpcap_init);
267 module_exit(led_cpcap_exit);
269 MODULE_DESCRIPTION("CPCAP Lighting driver");
270 MODULE_AUTHOR("Dan Murphy <D.Murphy@Motorola.com>");
271 MODULE_LICENSE("GPL");