video: sprdfd: disable ESD feature
[profile/mobile/platform/kernel/linux-3.10-sc7730.git] / drivers / leds / leds-sprd-kb.c
1 /*
2  * Copyright (C) 2012 Spreadtrum Communications Inc.
3  *
4  * This software is licensed under the terms of the GNU General Public
5  * License version 2, as published by the Free Software Foundation, and
6  * may be copied, distributed, and modified under those terms.
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 #include <linux/kernel.h>
14 #include <linux/module.h>
15 #include <linux/init.h>
16 #include <linux/platform_device.h>
17 #include <linux/leds.h>
18 #include <linux/err.h>
19 #include <linux/delay.h>
20 #include <linux/slab.h>
21 #include <soc/sprd/adi.h>
22 #include <soc/sprd/adc.h>
23 #include <soc/sprd/sci_glb_regs.h>
24 #ifdef CONFIG_HAS_EARLYSUSPEND
25 #include <linux/earlysuspend.h>
26 #endif
27
28 //#define SPRD_KPLED_DBG
29 #ifdef SPRD_KPLED_DBG
30 #define ENTER printk(KERN_INFO "[SPRD_KPLED_DBG] func: %s  line: %04d\n", __func__, __LINE__);
31 #define PRINT_DBG(x...)  printk(KERN_INFO "[SPRD_KPLED_DBG] " x)
32 #define PRINT_INFO(x...)  printk(KERN_INFO "[SPRD_KPLED_INFO] " x)
33 #define PRINT_WARN(x...)  printk(KERN_INFO "[SPRD_KPLED_WARN] " x)
34 #define PRINT_ERR(format,x...)  printk(KERN_ERR "[SPRD_KPLED_ERR] func: %s  line: %04d  info: " format, __func__, __LINE__, ## x)
35 #else
36 #define ENTER
37 #define PRINT_DBG(x...)
38 #define PRINT_INFO(x...)  printk(KERN_INFO "[SPRD_KPLED_INFO] " x)
39 #define PRINT_WARN(x...)  printk(KERN_INFO "[SPRD_KPLED_WARN] " x)
40 #define PRINT_ERR(format,x...)  printk(KERN_ERR "[SPRD_KPLED_ERR] func: %s  line: %04d  info: " format, __func__, __LINE__, ## x)
41 #endif
42
43 #ifdef CONFIG_ARCH_SCX35
44 #define SPRD_ANA_BASE           (SPRD_MISC_BASE + 0x8800)
45 #else
46 #define SPRD_ANA_BASE           (SPRD_MISC_BASE + 0x600)
47 #endif
48 #define ANA_REG_BASE            SPRD_ANA_BASE
49
50 #ifdef CONFIG_ARCH_SC8825
51 #define ANA_LED_CTRL           (ANA_REG_BASE + 0X70)
52 #else
53 #ifdef CONFIG_ARCH_SCX15
54 #define ANA_LED_CTRL           (ANA_REG_BASE + 0XE0)
55 #else
56 #ifdef CONFIG_ARCH_SCX35
57 #define ANA_LED_CTRL           (ANA_REG_BASE + 0XA0)
58 #ifdef CONFIG_ARCH_SCX30G
59 #define CA_CTRL2          (ANA_REG_BASE + 0X158)
60 #define LDO_KPLED_PD       (1 << 8)
61 #define SLP_LDOKPLED_PD_EN       (1 << 9)
62 #endif
63 #else
64 #define ANA_LED_CTRL           (ANA_REG_BASE + 0X68)
65 #endif
66 #endif
67 #endif
68
69 #define KPLED_CTL               ANA_LED_CTRL
70 #ifdef CONFIG_ARCH_SCX15
71 #define KPLED_PD_SET              (1 << 0)
72 #define KPLED_V_SHIFT            4
73 #define KPLED_V_MSK               (0x0F << KPLED_V_SHIFT)
74 #else
75 #ifdef CONFIG_ARCH_SCX35
76 #define KPLED_PD_SET            (1 << 1)
77 #define KPLED_V_SHIFT           4
78 #define KPLED_V_MSK             (0x0F << KPLED_V_SHIFT)
79 #else
80 #define KPLED_PD_SET            (1 << 11)
81 #define KPLED_PD_RST            (1 << 12)
82 #define KPLED_V_SHIFT           7
83 #define KPLED_V_MSK             (0x07 << KPLED_V_SHIFT)
84 #endif
85 #endif
86
87 /* sprd keypad backlight */
88 struct sprd_kpled {
89         struct platform_device *dev;
90         struct mutex mutex;
91         struct work_struct work;
92         spinlock_t value_lock;
93         enum led_brightness value;
94         struct led_classdev cdev;
95         int enabled;
96 #ifdef CONFIG_HAS_EARLYSUSPEND
97         struct early_suspend    early_suspend;
98 #endif
99
100 };
101
102 #define to_sprd_led(led_cdev) \
103         container_of(led_cdev, struct sprd_kpled, cdev)
104
105 static inline unsigned long kpled_read(unsigned long reg)
106 {
107         return sci_adi_read(reg);
108 }
109         
110 static void sprd_kpled_set_brightness( unsigned long  brightness)
111 {
112         unsigned long brightness_level;
113         brightness_level = brightness;
114
115         if(brightness_level > 255)
116                 brightness_level = 255;
117         
118 #ifdef CONFIG_ARCH_SCX35
119         /*brightness steps = 16*/
120         brightness_level = brightness_level/16;
121         brightness_level = 0;//set brightness_level = 0 for reducing power consumption
122 #else
123         /*brightness steps = 8*/
124         brightness_level = brightness_level/32;
125 #endif
126         
127         // Set Output Current
128         sci_adi_write((unsigned long)KPLED_CTL, ((brightness_level << KPLED_V_SHIFT) & KPLED_V_MSK), KPLED_V_MSK);
129         PRINT_INFO("reg:0x%08X set_val:0x%08X  brightness:%ld  brightness_level:%ld(0~15)\n", \
130                 KPLED_CTL, kpled_read((unsigned long)KPLED_CTL), brightness, brightness_level);
131 }
132
133 static void sprd_kpled_enable(struct sprd_kpled *led)
134 {
135 #ifdef CONFIG_ARCH_SCX35
136         sci_adi_clr((unsigned long)KPLED_CTL, KPLED_PD_SET);
137         #ifdef CONFIG_ARCH_SCX30G
138         sci_adi_set((unsigned long)CA_CTRL2, LDO_KPLED_PD);
139         #endif
140 #else
141         sci_adi_clr((unsigned long)KPLED_CTL, KPLED_PD_SET|KPLED_PD_RST);
142         sci_adi_set((unsigned long)KPLED_CTL, KPLED_PD_RST);
143 #endif
144
145         PRINT_INFO("sprd_kpled_enable\n");
146         sprd_kpled_set_brightness(led->value);
147         led->enabled = 1;
148 }
149
150 static void sprd_kpled_disable(struct sprd_kpled *led)
151 {
152 #ifdef CONFIG_ARCH_SCX35
153         sci_adi_set((unsigned long)KPLED_CTL, KPLED_PD_SET);
154         #ifdef CONFIG_ARCH_SCX30G
155         sci_adi_clr((unsigned long)CA_CTRL2, LDO_KPLED_PD);
156         #endif
157 #else
158         sci_adi_clr((unsigned long)KPLED_CTL, KPLED_PD_SET|KPLED_PD_RST);
159         sci_adi_set((unsigned long)KPLED_CTL, KPLED_PD_SET);
160 #endif
161
162         PRINT_INFO("sprd_kpled_disable\n");
163         led->enabled = 0;
164 }
165
166 static void sprd_kpled_work(struct work_struct *work)
167 {
168         struct sprd_kpled *led = container_of(work, struct sprd_kpled, work);
169         unsigned long flags;
170
171         mutex_lock(&led->mutex);
172         spin_lock_irqsave(&led->value_lock, flags);
173         if (led->value == LED_OFF) {
174                 spin_unlock_irqrestore(&led->value_lock, flags);
175                 sprd_kpled_disable(led);
176                 goto out;
177         }
178         spin_unlock_irqrestore(&led->value_lock, flags);
179         sprd_kpled_enable(led);
180 out:
181         mutex_unlock(&led->mutex);
182 }
183
184 static void sprd_kpled_set(struct led_classdev *led_cdev,
185                            enum led_brightness value)
186 {
187         struct sprd_kpled *led = to_sprd_led(led_cdev);
188         unsigned long flags;
189         
190         spin_lock_irqsave(&led->value_lock, flags);
191         led->value = value;
192         spin_unlock_irqrestore(&led->value_lock, flags);
193
194         schedule_work(&led->work);      
195 }
196
197 static void sprd_kpled_shutdown(struct platform_device *dev)
198 {
199         struct sprd_kpled *led = platform_get_drvdata(dev);
200
201         mutex_lock(&led->mutex);
202         sprd_kpled_disable(led);
203         mutex_unlock(&led->mutex);
204 }
205
206 #ifdef CONFIG_EARLYSUSPEND
207 static void sprd_kpled_early_suspend(struct early_suspend *es)
208 {
209         struct sprd_kpled *led = container_of(es, struct sprd_kpled, early_suspend);
210         PRINT_INFO("sprd_kpled_early_suspend\n");
211 }
212
213 static void sprd_kpled_late_resume(struct early_suspend *es)
214 {
215         struct sprd_kpled *led = container_of(es, struct sprd_kpled, early_suspend);
216         PRINT_INFO("sprd_kpled_late_resume\n");
217 }
218 #endif
219
220
221 static int sprd_kpled_probe(struct platform_device *dev)
222 {
223         struct sprd_kpled *led;
224         int ret;
225
226         led = kzalloc(sizeof(struct sprd_kpled), GFP_KERNEL);
227         if (led == NULL) {
228                 dev_err(&dev->dev, "No memory for device\n");
229                 return -ENOMEM;
230         }
231
232         led->cdev.brightness_set = sprd_kpled_set;
233         //led->cdev.default_trigger = "heartbeat";
234         led->cdev.default_trigger = "none";
235         led->cdev.name = "keyboard-backlight";
236         led->cdev.brightness_get = NULL;
237         led->cdev.flags |= LED_CORE_SUSPENDRESUME;
238         led->enabled = 0;
239
240         spin_lock_init(&led->value_lock);
241         mutex_init(&led->mutex);
242         INIT_WORK(&led->work, sprd_kpled_work);
243         led->value = LED_OFF;
244         platform_set_drvdata(dev, led);
245
246         /* register our new led device */
247
248         ret = led_classdev_register(&dev->dev, &led->cdev);
249         if (ret < 0) {
250                 dev_err(&dev->dev, "led_classdev_register failed\n");
251                 kfree(led);
252                 return ret;
253         }
254
255 #ifdef CONFIG_HAS_EARLYSUSPEND
256         led->early_suspend.suspend = sprd_kpled_early_suspend;
257         led->early_suspend.resume  = sprd_kpled_late_resume;
258         led->early_suspend.level   = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;
259         register_early_suspend(&led->early_suspend);
260 #endif
261
262         sprd_kpled_disable(led);//disabled by default
263
264 #ifdef CONFIG_ARCH_SCX30G
265         sci_adi_clr((unsigned long)CA_CTRL2, SLP_LDOKPLED_PD_EN);
266 #endif
267
268         return 0;
269 }
270
271 static int sprd_kpled_remove(struct platform_device *dev)
272 {
273         struct sprd_kpled *led = platform_get_drvdata(dev);
274
275         led_classdev_unregister(&led->cdev);
276         flush_scheduled_work();
277         led->value = LED_OFF;
278         led->enabled = 1;
279         sprd_kpled_disable(led);
280         kfree(led);
281
282         return 0;
283 }
284
285 static const struct of_device_id keyboard_backlight_of_match[] = {
286         { .compatible = "sprd,keyboard-backlight", },
287         { }
288 };
289
290 static struct platform_driver sprd_kpled_driver = {
291         .driver = {
292                 .name  = "keyboard-backlight",
293                 .owner = THIS_MODULE,
294                 .of_match_table = keyboard_backlight_of_match,
295         },
296         .probe    = sprd_kpled_probe,
297         .remove   = sprd_kpled_remove,
298         .shutdown = sprd_kpled_shutdown,
299 };
300
301 static int __init sprd_kpled_init(void)
302 {
303         return platform_driver_register(&sprd_kpled_driver);
304 }
305
306 static void sprd_kpled_exit(void)
307 {
308         platform_driver_unregister(&sprd_kpled_driver);
309 }
310
311 module_init(sprd_kpled_init);
312 module_exit(sprd_kpled_exit);
313
314 MODULE_AUTHOR("Ye Wu <ye.wu@spreadtrum.com>");
315 MODULE_DESCRIPTION("Sprd Keyboard backlight driver");
316 MODULE_LICENSE("GPL");
317 MODULE_ALIAS("platform:keyboard-backlight");
318