ARM: tizen_tm1_defconfig: Enable missing features related with CGROUPS
[profile/mobile/platform/kernel/linux-3.10-sc7730.git] / kernel / swap / energy / lcd / lcd_base.c
1 /*
2  *  Dynamic Binary Instrumentation Module based on KProbes
3  *  energy/lcd/lcd_base.c
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  *
19  * Copyright (C) Samsung Electronics, 2013
20  *
21  * 2013         Vyacheslav Cherkashin <v.cherkashin@samsung.com>
22  *
23  */
24
25
26 #include <linux/module.h>
27 #include <linux/slab.h>
28 #include <linux/fs.h>
29 #include <linux/fb.h>
30 #include <energy/tm_stat.h>
31 #include <energy/debugfs_energy.h>
32 #include "lcd_base.h"
33 #include "lcd_debugfs.h"
34
35
36 /**
37  * @brief Read the number of file
38  *
39  * @param path of the file
40  * @return Value or error(when negative)
41  */
42 int read_val(const char *path)
43 {
44         int ret;
45         struct file *f;
46         unsigned long val;
47         enum { buf_len = 32 };
48         char buf[buf_len];
49
50         f = filp_open(path, O_RDONLY, 0);
51         if (IS_ERR(f)) {
52                 printk(KERN_INFO "cannot open file \'%s\'", path);
53                 return PTR_ERR(f);
54         }
55
56         ret = kernel_read(f, 0, buf, sizeof(buf));
57         filp_close(f, NULL);
58         if (ret < 0)
59                 return ret;
60
61         buf[ret >= buf_len ? buf_len - 1 : ret] = '\0';
62
63         ret = strict_strtoul(buf, 0, &val);
64         if (ret)
65                 return ret;
66
67         return (int)val;
68 }
69
70 enum {
71         brt_no_init = -1,
72         brt_cnt = 10
73 };
74
75 enum power_t {
76         PW_ON,
77         PW_OFF
78 };
79
80 struct lcd_priv_data {
81         int min_brt;
82         int max_brt;
83
84         size_t tms_brt_cnt;
85         struct tm_stat *tms_brt;
86         spinlock_t lock_tms;
87         int brt_old;
88         enum power_t power;
89
90         u64 min_denom;
91         u64 min_num;
92         u64 max_denom;
93         u64 max_num;
94 };
95
96 static void *create_lcd_priv(struct lcd_ops *ops, size_t tms_brt_cnt)
97 {
98         int i;
99         struct lcd_priv_data *lcd;
100
101         if (tms_brt_cnt <= 0) {
102                 printk(KERN_INFO "error variable tms_brt_cnt=%d\n",
103                        tms_brt_cnt);
104                 return NULL;
105         }
106
107         lcd = kmalloc(sizeof(*lcd) + sizeof(*lcd->tms_brt) * tms_brt_cnt,
108                       GFP_KERNEL);
109         if (lcd == NULL) {
110                 printk(KERN_INFO "error: %s - out of memory\n", __func__);
111                 return NULL;
112         }
113
114         lcd->tms_brt = (void *)lcd + sizeof(*lcd);
115         lcd->tms_brt_cnt = tms_brt_cnt;
116
117         lcd->min_brt = ops->get(ops, LPD_MIN_BRIGHTNESS);
118         lcd->max_brt = ops->get(ops, LPD_MAX_BRIGHTNESS);
119
120         for (i = 0; i < tms_brt_cnt; ++i)
121                 tm_stat_init(&lcd->tms_brt[i]);
122
123         spin_lock_init(&lcd->lock_tms);
124
125         lcd->brt_old = brt_no_init;
126         lcd->power = PW_OFF;
127
128         lcd->min_denom = 1;
129         lcd->min_num = 1;
130         lcd->max_denom = 1;
131         lcd->max_num = 1;
132
133         return (void *)lcd;
134 }
135
136 static void destroy_lcd_priv(void *data)
137 {
138         kfree(data);
139 }
140
141 static struct lcd_priv_data *get_lcd_priv(struct lcd_ops *ops)
142 {
143         return (struct lcd_priv_data *)ops->priv;
144 }
145
146 static void clean_brightness(struct lcd_ops *ops)
147 {
148         struct lcd_priv_data *lcd = get_lcd_priv(ops);
149         int i;
150
151         spin_lock(&lcd->lock_tms);
152         for (i = 0; i < lcd->tms_brt_cnt; ++i)
153                 tm_stat_init(&lcd->tms_brt[i]);
154
155         lcd->brt_old = brt_no_init;
156         spin_unlock(&lcd->lock_tms);
157 }
158
159 static int get_brt_num_of_array(struct lcd_priv_data *lcd, int brt)
160 {
161         if (brt > lcd->max_brt || brt < lcd->min_brt) {
162                 printk(KERN_INFO "LCD energy error: set brightness=%d, "
163                        "when brightness[%d..%d]\n",
164                        brt, lcd->min_brt, lcd->max_brt);
165                 brt = brt > lcd->max_brt ? lcd->max_brt : lcd->min_brt;
166         }
167
168         return lcd->tms_brt_cnt * (brt - lcd->min_brt) /
169                (lcd->max_brt - lcd->min_brt + 1);
170 }
171
172 static void set_brightness(struct lcd_ops *ops, int brt)
173 {
174         struct lcd_priv_data *lcd = get_lcd_priv(ops);
175         int n = get_brt_num_of_array(lcd, brt);
176
177         spin_lock(&lcd->lock_tms);
178
179         if (lcd->power == PW_ON && lcd->brt_old != n) {
180                 u64 time = get_ntime();
181                 if (lcd->brt_old != brt_no_init)
182                         tm_stat_update(&lcd->tms_brt[lcd->brt_old], time);
183
184                 tm_stat_set_timestamp(&lcd->tms_brt[n], time);
185         }
186         lcd->brt_old = n;
187
188         spin_unlock(&lcd->lock_tms);
189 }
190
191 static void set_power_on_set_brt(struct lcd_priv_data *lcd)
192 {
193         if (lcd->brt_old != brt_no_init) {
194                 u64 time = get_ntime();
195                 tm_stat_set_timestamp(&lcd->tms_brt[lcd->brt_old], time);
196         }
197 }
198
199 static void set_power_on(struct lcd_priv_data *lcd)
200 {
201         if (lcd->power == PW_OFF)
202                 set_power_on_set_brt(lcd);
203
204         lcd->power = PW_ON;
205 }
206
207 static void set_power_off_update_brt(struct lcd_priv_data *lcd)
208 {
209         if (lcd->brt_old != brt_no_init) {
210                 u64 time = get_ntime();
211                 tm_stat_update(&lcd->tms_brt[lcd->brt_old], time);
212                 lcd->brt_old = brt_no_init;
213         }
214 }
215
216 static void set_power_off(struct lcd_priv_data *lcd)
217 {
218         if (lcd->power == PW_ON)
219                 set_power_off_update_brt(lcd);
220
221         lcd->power = PW_OFF;
222 }
223
224 static void set_power(struct lcd_ops *ops, int val)
225 {
226         struct lcd_priv_data *lcd = get_lcd_priv(ops);
227
228         spin_lock(&lcd->lock_tms);
229
230         switch (val) {
231         case FB_BLANK_UNBLANK:
232                 set_power_on(lcd);
233                 break;
234         case FB_BLANK_POWERDOWN:
235                 set_power_off(lcd);
236                 break;
237         default:
238                 printk(KERN_INFO "LCD energy error: set power=%d\n", val);
239                 break;
240         }
241
242         spin_unlock(&lcd->lock_tms);
243 }
244
245 static int func_notifier_lcd(struct lcd_ops *ops, enum lcd_action_type action,
246                              void *data)
247 {
248         switch (action) {
249         case LAT_BRIGHTNESS:
250                 set_brightness(ops, (int)data);
251                 break;
252         case LAT_POWER:
253                 set_power(ops, (int)data);
254                 break;
255         default:
256                 printk(KERN_INFO "LCD energy error: action=%d\n", action);
257                 return -EINVAL;
258         }
259
260         return 0;
261 }
262
263 /**
264  * @brief Get the array size of LCD
265  *
266  * @param ops LCD operations
267  * @return Array size
268  */
269 size_t get_lcd_size_array(struct lcd_ops *ops)
270 {
271         struct lcd_priv_data *lcd = get_lcd_priv(ops);
272
273         return lcd->tms_brt_cnt;
274 }
275
276 /**
277  * @brief Get an array of times
278  *
279  * @param ops LCD operations
280  * @param array_time[out] Array of times
281  * @return Void
282  */
283 void get_lcd_array_time(struct lcd_ops *ops, u64 *array_time)
284 {
285         struct lcd_priv_data *lcd = get_lcd_priv(ops);
286         int i;
287
288         spin_lock(&lcd->lock_tms);
289         for (i = 0; i < lcd->tms_brt_cnt; ++i)
290                 array_time[i] = tm_stat_running(&lcd->tms_brt[i]);
291
292         if (lcd->power == PW_ON && lcd->brt_old != brt_no_init) {
293                 int old = lcd->brt_old;
294                 struct tm_stat *tm = &lcd->tms_brt[old];
295
296                 array_time[old] += get_ntime() - tm_stat_timestamp(tm);
297         }
298         spin_unlock(&lcd->lock_tms);
299 }
300
301 static int register_lcd(struct lcd_ops *ops)
302 {
303         int ret = 0;
304
305         ops->priv = create_lcd_priv(ops, brt_cnt);
306
307         /* TODO: create init_func() for 'struct rational' */
308         ops->min_coef.num = 1;
309         ops->min_coef.denom = 1;
310         ops->max_coef.num = 1;
311         ops->max_coef.denom = 1;
312
313         ops->notifier = func_notifier_lcd;
314
315         ret = register_lcd_debugfs(ops);
316         if (ret)
317                 destroy_lcd_priv(ops->priv);
318
319         return ret;
320 }
321
322 static void unregister_lcd(struct lcd_ops *ops)
323 {
324         unregister_lcd_debugfs(ops);
325         destroy_lcd_priv(ops->priv);
326 }
327
328
329
330
331 /* ============================================================================
332  * ===                          LCD_INIT/LCD_EXIT                           ===
333  * ============================================================================
334  */
335 typedef struct lcd_ops *(*get_ops_t)(void);
336
337 DEFINITION_LCD_FUNC;
338
339 get_ops_t lcd_ops[] = DEFINITION_LCD_ARRAY;
340 enum { lcd_ops_cnt = sizeof(lcd_ops) / sizeof(get_ops_t) };
341
342 enum ST_LCD_OPS {
343         SLO_REGISTER    = 1 << 0,
344         SLO_SET         = 1 << 1
345 };
346
347 static DEFINE_MUTEX(lcd_lock);
348 static enum ST_LCD_OPS stat_lcd_ops[lcd_ops_cnt];
349
350 static void do_lcd_exit(void)
351 {
352         int i;
353         struct lcd_ops *ops;
354
355         mutex_lock(&lcd_lock);
356         for (i = 0; i < lcd_ops_cnt; ++i) {
357                 ops = lcd_ops[i]();
358
359                 if (stat_lcd_ops[i] & SLO_SET) {
360                         ops->unset(ops);
361                         stat_lcd_ops[i] &= ~SLO_SET;
362                 }
363
364                 if (stat_lcd_ops[i] & SLO_REGISTER) {
365                         unregister_lcd(ops);
366                         stat_lcd_ops[i] &= ~SLO_REGISTER;
367                 }
368         }
369         mutex_unlock(&lcd_lock);
370 }
371
372 /**
373  * @brief LCD deinitialization
374  *
375  * @return Void
376  */
377 void lcd_exit(void)
378 {
379         do_lcd_exit();
380 }
381
382 static int do_lcd_init(void)
383 {
384         int i, ret, count = 0;
385         struct lcd_ops *ops;
386
387         mutex_lock(&lcd_lock);
388         for (i = 0; i < lcd_ops_cnt; ++i) {
389                 ops = lcd_ops[i]();
390                 if (ops == NULL) {
391                         printk(KERN_INFO "error %s [ops == NULL]\n", __func__);
392                         continue;
393                 }
394
395                 if (0 == ops->check(ops)) {
396                         printk(KERN_INFO "error checking %s\n", ops->name);
397                         continue;
398                 }
399
400                 ret = register_lcd(ops);
401                 if (ret) {
402                         printk(KERN_INFO "error register_lcd %s\n", ops->name);
403                         continue;
404                 }
405
406                 stat_lcd_ops[i] |= SLO_REGISTER;
407                 ++count;
408         }
409         mutex_unlock(&lcd_lock);
410
411         return count ? 0 : -EPERM;
412 }
413
414 /**
415  * @brief LCD initialization
416  *
417  * @return Error code
418  */
419 int lcd_init(void)
420 {
421         int ret;
422
423         ret = do_lcd_init();
424         if (ret)
425                 printk(KERN_INFO "LCD is not supported\n");
426
427         return ret;
428 }
429
430
431
432 /* ============================================================================
433  * ===                     LCD_SET_ENERGY/LCD_UNSET_ENERGY                  ===
434  * ============================================================================
435  */
436
437 /**
438  * @brief Start measuring the energy consumption of LСD
439  *
440  * @return Error code
441  */
442 int lcd_set_energy(void)
443 {
444         int i, ret, count = 0;
445         struct lcd_ops *ops;
446
447         mutex_lock(&lcd_lock);
448         for (i = 0; i < lcd_ops_cnt; ++i) {
449                 ops = lcd_ops[i]();
450                 if (stat_lcd_ops[i] & SLO_REGISTER) {
451                         ret = ops->set(ops);
452                         if (ret) {
453                                 printk(KERN_INFO "error %s set LCD energy",
454                                        ops->name);
455                                 continue;
456                         }
457
458                         set_brightness(ops, ops->get(ops, LPD_BRIGHTNESS));
459                         set_power(ops, ops->get(ops, LPD_POWER));
460
461                         stat_lcd_ops[i] |= SLO_SET;
462                         ++count;
463                 }
464         }
465         mutex_unlock(&lcd_lock);
466
467         return count ? 0 : -EPERM;
468 }
469
470 /**
471  * @brief Stop measuring the energy consumption of LСD
472  *
473  * @return Void
474  */
475 void lcd_unset_energy(void)
476 {
477         int i, ret;
478         struct lcd_ops *ops;
479
480         mutex_lock(&lcd_lock);
481         for (i = 0; i < lcd_ops_cnt; ++i) {
482                 ops = lcd_ops[i]();
483                 if (stat_lcd_ops[i] & SLO_SET) {
484                         ret = ops->unset(ops);
485                         if (ret)
486                                 printk(KERN_INFO "error %s unset LCD energy",
487                                        ops->name);
488
489                         clean_brightness(ops);
490                         stat_lcd_ops[i] &= ~SLO_SET;
491                 }
492         }
493         mutex_unlock(&lcd_lock);
494 }