temp revert rk change
[firefly-linux-kernel-4.4.55.git] / drivers / leds / leds-ld-cpcap.c
1 /*
2  * Copyright (C) 2010 Motorola, Inc.
3  *
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.
7  *
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.
12  *
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
16  * 02111-1307, USA
17  */
18
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>
28
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;
36         int regulator_state;
37         short blink_val;
38 };
39
40 static void cpcap_set(struct led_classdev *led_cdev,
41                             enum led_brightness brightness)
42 {
43         struct cpcap_led_data *cpcap_led_data =
44                 container_of(led_cdev, struct cpcap_led_data,
45                 cpcap_class_dev);
46
47         if (brightness > 255)
48                 brightness = 255;
49
50         cpcap_led_data->brightness = brightness;
51         queue_work(system_nrt_wq, &cpcap_led_data->brightness_work);
52 }
53 EXPORT_SYMBOL(cpcap_set);
54
55 static void
56 cpcap_led_set_blink(struct cpcap_led_data *info, unsigned long blink)
57 {
58         info->blink_val = blink;
59
60         if (info->pdata->blink_able) {
61                 if(info->blink_val) {
62                         cpcap_uc_start(info->cpcap, CPCAP_MACRO_6);
63                 } else {
64                         cpcap_uc_stop(info->cpcap, CPCAP_MACRO_6);
65                         queue_work(system_nrt_wq, &info->brightness_work);
66                 }
67         }
68 }
69
70 static int cpcap_led_blink(struct led_classdev *led_cdev,
71                                unsigned long *delay_on,
72                                unsigned long *delay_off)
73 {
74         struct cpcap_led_data *info =
75                 container_of(led_cdev, struct cpcap_led_data,
76                          cpcap_class_dev);
77
78         cpcap_led_set_blink(info, *delay_on);
79         return 0;
80 }
81
82 static void cpcap_brightness_work(struct work_struct *work)
83 {
84         int cpcap_status = 0;
85         unsigned short brightness = 0;
86
87         struct cpcap_led_data *cpcap_led_data =
88             container_of(work, struct cpcap_led_data, brightness_work);
89
90         brightness = cpcap_led_data->brightness;
91
92         if (brightness > 0) {
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 |
96                                  0x01);
97
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;
102                 }
103
104                 cpcap_status = cpcap_regacc_write(cpcap_led_data->cpcap,
105                                 cpcap_led_data->pdata->cpcap_register,
106                                 brightness,
107                                 cpcap_led_data->pdata->cpcap_reg_mask);
108
109                 if (cpcap_status < 0)
110                         pr_err("%s: Writing to the register failed for %i\n",
111                                __func__, cpcap_status);
112
113         } else {
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;
118                 }
119                 /* Due to a HW issue turn off the current then
120                 turn off the duty cycle */
121                 brightness = 0x01;
122                 cpcap_status = cpcap_regacc_write(cpcap_led_data->cpcap,
123                                 cpcap_led_data->pdata->cpcap_register,
124                                 brightness,
125                                 cpcap_led_data->pdata->cpcap_reg_mask);
126
127
128                 brightness = 0x00;
129                 cpcap_status = cpcap_regacc_write(cpcap_led_data->cpcap,
130                                 cpcap_led_data->pdata->cpcap_register,
131                                 brightness,
132                                 cpcap_led_data->pdata->cpcap_reg_mask);
133
134
135                 if (cpcap_status < 0)
136                         pr_err("%s: Writing to the register failed for %i\n",
137                                __func__, cpcap_status);
138
139         }
140 }
141
142 static ssize_t blink_show(struct device *dev,
143                 struct device_attribute *attr, char *buf)
144 {
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);
148
149         return sprintf(buf, "%u\n", info->blink_val);
150 }
151
152 static ssize_t blink_store(struct device *dev,
153                 struct device_attribute *attr, const char *buf, size_t size)
154 {
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);
160         return size;
161 }
162
163 static DEVICE_ATTR(blink, 0644, blink_show, blink_store);
164
165 static int cpcap_probe(struct platform_device *pdev)
166 {
167         int ret = 0;
168         struct cpcap_led_data *info;
169
170         if (pdev == NULL) {
171                 pr_err("%s: platform data required\n", __func__);
172                 return -ENODEV;
173
174         }
175         info = kzalloc(sizeof(struct cpcap_led_data), GFP_KERNEL);
176         if (info == NULL) {
177                 ret = -ENOMEM;
178                 return ret;
179         }
180
181         info->pdata = pdev->dev.platform_data;
182         info->cpcap = platform_get_drvdata(pdev);
183         platform_set_drvdata(pdev, info);
184
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;
193
194                 }
195         }
196         info->regulator_state = 0;
197
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";
205
206         ret = led_classdev_register(&pdev->dev, &info->cpcap_class_dev);
207         if (ret < 0) {
208                 pr_err("%s:Register %s class failed\n",
209                         __func__, info->cpcap_class_dev.name);
210                 goto err_reg_button_class_failed;
211         }
212
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.
216          */
217         ret = device_create_file(info->cpcap_class_dev.dev, &dev_attr_blink);
218         if (ret < 0) {
219                 pr_err("%s:device_create_file failed for blink\n", __func__);
220                 goto err_device_create_file_failed;
221         }
222
223         INIT_WORK(&info->brightness_work, cpcap_brightness_work);
224
225         return ret;
226
227 err_device_create_file_failed:
228         led_classdev_unregister(&info->cpcap_class_dev);
229 err_reg_button_class_failed:
230         if (info->regulator)
231                 regulator_put(info->regulator);
232 exit_request_reg_failed:
233         kfree(info);
234         return ret;
235 }
236
237 static int cpcap_remove(struct platform_device *pdev)
238 {
239         struct cpcap_led_data *info = platform_get_drvdata(pdev);
240
241         device_remove_file(info->cpcap_class_dev.dev, &dev_attr_blink);
242         if (info->regulator)
243                 regulator_put(info->regulator);
244         led_classdev_unregister(&info->cpcap_class_dev);
245         return 0;
246 }
247
248 static struct platform_driver ld_cpcap_driver = {
249         .probe = cpcap_probe,
250         .remove = cpcap_remove,
251         .driver = {
252                    .name = LD_CPCAP_LED_DRV,
253         },
254 };
255
256 static int __init led_cpcap_init(void)
257 {
258         return platform_driver_register(&ld_cpcap_driver);
259 }
260
261 static void __exit led_cpcap_exit(void)
262 {
263         platform_driver_unregister(&ld_cpcap_driver);
264 }
265
266 module_init(led_cpcap_init);
267 module_exit(led_cpcap_exit);
268
269 MODULE_DESCRIPTION("CPCAP Lighting driver");
270 MODULE_AUTHOR("Dan Murphy <D.Murphy@Motorola.com>");
271 MODULE_LICENSE("GPL");