Initialize Tizen 2.3
[framework/system/deviced.git] / src / display / auto-brightness.c
1 /*
2  * deviced
3  *
4  * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the License);
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19
20 #include <stdbool.h>
21 #include <stdio.h>
22 #include <math.h>
23 #include <limits.h>
24 #include <unistd.h>
25 #include <stdlib.h>
26 #include <sys/types.h>
27 #include <fcntl.h>
28 #include <vconf.h>
29 #include <sensor.h>
30 #include <Ecore.h>
31
32 #include "util.h"
33 #include "core.h"
34 #include "display-ops.h"
35 #include "device-node.h"
36 #include "proc/proc-handler.h"
37
38 #define DISP_FORCE_SHIFT        12
39 #define DISP_FORCE_CMD(prop, force)     (((force) << DISP_FORCE_SHIFT) | prop)
40
41 #define SAMPLING_INTERVAL       1       /* 1 sec */
42 #define MAX_SAMPLING_COUNT      3
43 #define MAX_FAULT               5
44 #define DEFAULT_AUTOMATIC_BRT   5
45 #define MAX_AUTOMATIC_COUNT     11
46 #define AUTOMATIC_DEVIDE_VAL    10
47 #define AUTOMATIC_DELAY_TIME    0.5     /* 0.5 sec */
48 #define MAX_NORMAL_LUX          10000
49
50 #define RADIAN_VALUE            (57.2957)
51 #define ROTATION_90             90
52 #define WORKING_ANGLE_MIN       0
53 #define WORKING_ANGLE_MAX       20
54
55 #define ISVALID_AUTOMATIC_INDEX(index) (index >= 0 && index < MAX_AUTOMATIC_COUNT)
56
57 static int (*_default_action) (int);
58 static Ecore_Timer *alc_timeout_id = 0;
59 static Ecore_Timer *update_timeout;
60 static int light_handle = -1;
61 static int accel_handle = -1;
62 static int fault_count = 0;
63 static int automatic_brt = DEFAULT_AUTOMATIC_BRT;
64 static int min_brightness = PM_MIN_BRIGHTNESS;
65 static char *min_brightness_name = 0;
66 static int last_autobrightness = 0;
67 static int value_table[MAX_AUTOMATIC_COUNT];
68
69 static bool update_working_position(void)
70 {
71         sensor_data_t data;
72         int ret;
73         float x, y, z, pitch, realg;
74
75         ret = sf_get_data(accel_handle, ACCELEROMETER_BASE_DATA_SET, &data);
76         if (ret < 0) {
77                 _E("Fail to get accelerometer data! %d", ret);
78                 return true;
79         }
80
81         x = data.values[0];
82         y = data.values[1];
83         z = data.values[2];
84
85         realg = (float)sqrt((x * x) + (y * y) + (z * z));
86         pitch = ROTATION_90 - abs((int) (asin(z / realg) * RADIAN_VALUE));
87
88         _D("accel data [%f, %f, %f] - %f", x, y, z, pitch);
89
90         if (pitch >= WORKING_ANGLE_MIN && pitch <= WORKING_ANGLE_MAX)
91                 return true;
92         return false;
93 }
94
95 static int get_siop_brightness(int value)
96 {
97         int cmd, ret, brt;
98
99         cmd = DISP_CMD(PROP_DISPLAY_MAX_BRIGHTNESS, DEFAULT_DISPLAY);
100         ret = device_get_property(DEVICE_TYPE_DISPLAY, cmd, &brt);
101         if (ret >= 0 && value > brt)
102                 return brt;
103
104         return value;
105 }
106
107 static void alc_set_brightness(int setting, int value, int lux)
108 {
109         static int old;
110         int position, cmd, tmp_value = 0;
111
112         cmd = DISP_CMD(PROP_DISPLAY_BRIGHTNESS, DEFAULT_DISPLAY);
113         if (device_get_property(DEVICE_TYPE_DISPLAY, cmd, &tmp_value) < 0) {
114                 _E("Fail to get display brightness!");
115                 return;
116         }
117
118         if (value < min_brightness)
119                 value = min_brightness;
120
121         if (cur_siop_level() != 0)
122                 value = get_siop_brightness(value);
123
124         if (tmp_value != value) {
125                 if (!setting && min_brightness == PM_MIN_BRIGHTNESS) {
126                         position = update_working_position();
127                         if (!position && (old > lux)) {
128                                 _D("It's not working position, "
129                                     "LCD isn't getting dark!");
130                                 return;
131                         }
132                 }
133                 int diff, step;
134
135                 diff = value - tmp_value;
136                 if (abs(diff) < display_conf.brightness_change_step)
137                         step = (diff > 0 ? 1 : -1);
138                 else
139                         step = (int)ceil(diff /
140                             (float)display_conf.brightness_change_step);
141
142                 _D("%d", step);
143                 while (tmp_value != value) {
144                         if (step == 0) break;
145
146                         tmp_value += step;
147                         if ((step > 0 && tmp_value > value) ||
148                             (step < 0 && tmp_value < value))
149                                 tmp_value = value;
150
151                         backlight_ops.set_default_brt(tmp_value);
152                         backlight_ops.update();
153                         last_autobrightness = tmp_value;
154                 }
155                 _I("load light data:%d lux,auto brt %d,min brightness %d,"
156                     "brightness %d", lux, automatic_brt, min_brightness, value);
157                 old = lux;
158         }
159 }
160
161 static bool check_brightness_changed(int value)
162 {
163         int i;
164         static int values[MAX_SAMPLING_COUNT], count = 0;
165
166         if (count >= MAX_SAMPLING_COUNT || count < 0)
167                 count = 0;
168
169         values[count++] = value;
170
171         for (i = 0; i < MAX_SAMPLING_COUNT - 1; i++)
172                 if (values[i] != values[i+1])
173                         return false;
174         return true;
175 }
176
177 static void set_brightness_direct(void)
178 {
179         int ret, cmd, value;
180         sensor_data_t light_data;
181
182         if (pm_cur_state != S_NORMAL || !get_hallic_open())
183                 return;
184
185         /* direct update if it's previous value */
186         if (last_autobrightness > 0) {
187                 backlight_ops.set_default_brt(last_autobrightness);
188                 backlight_ops.update();
189                 return;
190         }
191
192         /* get lux value from light sensor */
193         ret = sf_get_data(light_handle, LIGHT_LUX_DATA_SET, &light_data);
194         if (ret < 0 || (int)light_data.values[0] < 0)
195                 return;
196
197         /* get brightness by lux */
198         cmd = DISP_FORCE_CMD(PROP_DISPLAY_BRIGHTNESS_BY_LUX, true);
199         cmd = DISP_CMD(cmd, (int)light_data.values[0]);
200         ret = device_get_property(DEVICE_TYPE_DISPLAY, cmd, value_table);
201         if (ret < 0)
202                 return;
203
204         /* get auto brightness level from value table*/
205         value = (ISVALID_AUTOMATIC_INDEX(automatic_brt) ?
206             value_table[automatic_brt] : value_table[DEFAULT_AUTOMATIC_BRT]);
207
208         /* update brightness actually */
209         backlight_ops.set_default_brt(value);
210         backlight_ops.update();
211         last_autobrightness = value;
212 }
213
214 static bool check_hbm(int lux)
215 {
216         int ret, old_state, new_state;
217
218         ret = device_get_property(DEVICE_TYPE_DISPLAY,
219             PROP_DISPLAY_HBM_CONTROL, &old_state);
220         if (ret < 0) {
221                 _E("Failed to get HBM state!");
222                 return false;
223         }
224
225         if (old_state)
226                 new_state = (lux <= MAX_NORMAL_LUX ? 0 : 1);
227         else
228                 new_state = (lux >= display_conf.hbm_lux_threshold ? 1 : 0);
229
230         if (old_state != new_state) {
231                 _D("hbm is %s", (new_state ? "on" : "off"));
232                 ret = device_set_property(DEVICE_TYPE_DISPLAY,
233                     PROP_DISPLAY_HBM_CONTROL, new_state);
234                 if (ret < 0)
235                         _E("Failed to set HBM state!");
236                 /*
237                  * Brightness is changed directly
238                  * when hbm state is changed from on to off
239                  */
240                 if (!new_state)
241                         set_brightness_direct();
242         }
243
244         return (new_state ? true : false);
245 }
246
247 static bool alc_update_brt(bool setting)
248 {
249         int value = 0;
250         int cal_value = 0;
251         int ret = -1;
252         int cmd;
253         sensor_data_t light_data;
254
255         ret = sf_get_data(light_handle, LIGHT_LUX_DATA_SET, &light_data);
256         if (ret < 0 || (int)light_data.values[0] < 0) {
257                 fault_count++;
258         } else {
259                 int force = (setting ? 1 : 0);
260                 cmd = DISP_FORCE_CMD(PROP_DISPLAY_BRIGHTNESS_BY_LUX, force);
261                 cmd = DISP_CMD(cmd, (int)light_data.values[0]);
262                 ret = device_get_property(DEVICE_TYPE_DISPLAY, cmd, value_table);
263
264                 value = (ISVALID_AUTOMATIC_INDEX(automatic_brt) ?
265                     value_table[automatic_brt] : value_table[DEFAULT_AUTOMATIC_BRT]);
266
267                 if (ret < 0 || value < PM_MIN_BRIGHTNESS ||
268                     value > PM_MAX_BRIGHTNESS) {
269                         _E("fail to load light data : %d lux, %d",
270                                 (int)light_data.values[0], value);
271                         fault_count++;
272                 } else {
273                         fault_count = 0;
274
275                         /* Check HBM (High Brightness Mode) */
276                         if (check_hbm((int)light_data.values[0]))
277                                 return EINA_TRUE;
278
279                         if (!check_brightness_changed(value) && !setting)
280                                 return EINA_TRUE;
281
282                         alc_set_brightness(setting, value, (int)light_data.values[0]);
283                 }
284         }
285
286         if ((fault_count > MAX_FAULT) && !(pm_status_flag & PWROFF_FLAG)) {
287                 if (alc_timeout_id > 0)
288                         ecore_timer_del(alc_timeout_id);
289                 alc_timeout_id = NULL;
290                 vconf_set_int(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT,
291                     SETTING_BRIGHTNESS_AUTOMATIC_OFF);
292                 _E("Fault counts is over %d, disable automatic brightness",
293                     MAX_FAULT);
294                 return EINA_FALSE;
295         }
296         return EINA_TRUE;
297 }
298
299 static bool alc_handler(void* data)
300 {
301         if (pm_cur_state != S_NORMAL || !get_hallic_open()){
302                 if (alc_timeout_id > 0)
303                         ecore_timer_del(alc_timeout_id);
304                 alc_timeout_id = NULL;
305                 return EINA_FALSE;
306         }
307
308         if (alc_update_brt(false) == EINA_FALSE)
309                 return EINA_FALSE;
310
311         if (alc_timeout_id != 0)
312                 return EINA_TRUE;
313
314         return EINA_FALSE;
315 }
316
317 static int alc_action(int timeout)
318 {
319         /* sampling timer add */
320         if (alc_timeout_id == 0 && !(pm_status_flag & PWRSV_FLAG)) {
321                 display_info.update_auto_brightness(true);
322
323                 alc_timeout_id =
324                     ecore_timer_add(display_conf.lightsensor_interval,
325                             (Ecore_Task_Cb)alc_handler, NULL);
326         }
327
328         if (_default_action != NULL)
329                 return _default_action(timeout);
330
331         /* unreachable code */
332         return -1;
333 }
334
335 static int connect_sfsvc(void)
336 {
337         int sf_state = -1;
338
339         _I("connect with sensor fw");
340         /* light sensor */
341         light_handle = sf_connect(LIGHT_SENSOR);
342         if (light_handle < 0) {
343                 _E("light sensor attach fail");
344                 goto error;
345         }
346         sf_state = sf_start(light_handle, 0);
347         if (sf_state < 0) {
348                 _E("light sensor attach fail");
349                 sf_disconnect(light_handle);
350                 light_handle = -1;
351                 goto error;
352         }
353         /* accelerometer sensor */
354         accel_handle = sf_connect(ACCELEROMETER_SENSOR);
355         if (accel_handle < 0) {
356                 _E("accelerometer sensor attach fail");
357                 goto error;
358         }
359         sf_state = sf_start(accel_handle, 0);
360         if (sf_state < 0) {
361                 _E("accelerometer sensor attach fail");
362                 sf_disconnect(accel_handle);
363                 accel_handle = -1;
364                 goto error;
365         }
366
367         fault_count = 0;
368         return 0;
369
370 error:
371         if (light_handle >= 0) {
372                 sf_stop(light_handle);
373                 sf_disconnect(light_handle);
374                 light_handle = -1;
375         }
376         if (accel_handle >= 0) {
377                 sf_stop(accel_handle);
378                 sf_disconnect(accel_handle);
379                 accel_handle = -1;
380         }
381         return -EIO;
382 }
383
384 static int disconnect_sfsvc(void)
385 {
386         _I("disconnect with sensor fw");
387         /* light sensor*/
388         if(light_handle >= 0) {
389                 sf_stop(light_handle);
390                 sf_disconnect(light_handle);
391                 light_handle = -1;
392         }
393         /* accelerometer sensor*/
394         if(accel_handle >= 0) {
395                 sf_stop(accel_handle);
396                 sf_disconnect(accel_handle);
397                 accel_handle = -1;
398         }
399
400         if (_default_action != NULL) {
401                 states[S_NORMAL].action = _default_action;
402                 _default_action = NULL;
403         }
404         if (alc_timeout_id > 0) {
405                 ecore_timer_del(alc_timeout_id);
406                 alc_timeout_id = NULL;
407         }
408
409         return 0;
410 }
411
412 static inline void set_brtch_state(void)
413 {
414         if (pm_status_flag & PWRSV_FLAG) {
415                 pm_status_flag |= BRTCH_FLAG;
416                 vconf_set_bool(VCONFKEY_PM_BRIGHTNESS_CHANGED_IN_LPM, true);
417                 _D("brightness changed in low battery,"
418                     "escape dim state (light)");
419         }
420 }
421
422 static int set_autobrightness_state(int status)
423 {
424         int ret = -1;
425         int brt = -1;
426         int default_brt = -1;
427         int max_brt = -1;
428
429         if (status == SETTING_BRIGHTNESS_AUTOMATIC_ON) {
430                 if(connect_sfsvc() < 0)
431                         return -1;
432
433                 /* escape dim state if it's in low battery.*/
434                 set_brtch_state();
435
436                 /* change alc action func */
437                 if (_default_action == NULL)
438                         _default_action = states[S_NORMAL].action;
439                 states[S_NORMAL].action = alc_action;
440
441                 set_brightness_direct();
442                 alc_timeout_id =
443                     ecore_timer_add(display_conf.lightsensor_interval,
444                             (Ecore_Task_Cb)alc_handler, NULL);
445         } else if (status == SETTING_BRIGHTNESS_AUTOMATIC_PAUSE) {
446                 _I("auto brightness paused!");
447                 disconnect_sfsvc();
448                 backlight_ops.hbm_off();
449         } else {
450                 disconnect_sfsvc();
451                 backlight_ops.hbm_off();
452                 /* escape dim state if it's in low battery.*/
453                 set_brtch_state();
454
455                 ret = get_setting_brightness(&default_brt);
456                 if (ret != 0 || (default_brt < PM_MIN_BRIGHTNESS || default_brt > PM_MAX_BRIGHTNESS)) {
457                         _I("fail to read vconf value for brightness");
458                         brt = PM_DEFAULT_BRIGHTNESS;
459                         if(default_brt < PM_MIN_BRIGHTNESS || default_brt > PM_MAX_BRIGHTNESS)
460                                 vconf_set_int(VCONFKEY_SETAPPL_LCD_BRIGHTNESS, brt);
461                         default_brt = brt;
462                 }
463
464                 backlight_ops.set_default_brt(default_brt);
465                 backlight_ops.update();
466         }
467
468         return 0;
469 }
470
471 static void set_alc_function(keynode_t *key_nodes, void *data)
472 {
473         int status, ret;
474
475         if (key_nodes == NULL) {
476                 _E("wrong parameter, key_nodes is null");
477                 return;
478         }
479
480         status = vconf_keynode_get_int(key_nodes);
481
482         switch (status) {
483         case SETTING_BRIGHTNESS_AUTOMATIC_OFF:
484         case SETTING_BRIGHTNESS_AUTOMATIC_ON:
485         case SETTING_BRIGHTNESS_AUTOMATIC_PAUSE:
486                 ret = set_autobrightness_state(status);
487                 _D("set auto brightness : %d", ret);
488                 break;
489         default:
490                 _E("invalid value! %d", status);
491         }
492 }
493
494 static bool check_sfsvc(void* data)
495 {
496         /* this function will return opposite value for re-callback in fail */
497         int vconf_auto;
498         int sf_state = 0;
499
500         _I("register sfsvc");
501
502         vconf_get_int(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT, &vconf_auto);
503         if (vconf_auto == SETTING_BRIGHTNESS_AUTOMATIC_ON) {
504                 if(connect_sfsvc() < 0)
505                         return EINA_TRUE;
506
507                 /* change alc action func */
508                 if (_default_action == NULL)
509                         _default_action = states[S_NORMAL].action;
510                 states[S_NORMAL].action = alc_action;
511                 alc_timeout_id =
512                     ecore_timer_add(display_conf.lightsensor_interval,
513                             (Ecore_Task_Cb)alc_handler, NULL);
514                 if (alc_timeout_id > 0)
515                         return EINA_FALSE;
516                 disconnect_sfsvc();
517                 return EINA_TRUE;
518         }
519         _I("change vconf value before registering sfsvc");
520         return EINA_FALSE;
521 }
522
523 static void set_alc_automatic_brt(keynode_t *key_nodes, void *data)
524 {
525         if (key_nodes == NULL) {
526                 _E("wrong parameter, key_nodes is null");
527                 return;
528         }
529         automatic_brt = vconf_keynode_get_int(key_nodes) / AUTOMATIC_DEVIDE_VAL;
530         _D("automatic brt : %d", automatic_brt);
531
532         alc_update_brt(true);
533 }
534
535 static Eina_Bool update_handler(void* data)
536 {
537         int ret, on;
538
539         update_timeout = NULL;
540
541         if (pm_cur_state != S_NORMAL)
542                 return EINA_FALSE;
543
544         if (!get_hallic_open())
545                 return EINA_FALSE;
546
547         ret = vconf_get_int(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT, &on);
548         if (ret < 0 || on != SETTING_BRIGHTNESS_AUTOMATIC_ON)
549                 return EINA_FALSE;
550
551         _D("auto brightness is working!");
552
553         sf_change_sensor_option(light_handle, 1);
554         alc_update_brt(true);
555         sf_change_sensor_option(light_handle, 0);
556
557         return EINA_FALSE;
558 }
559
560 static void update_auto_brightness(bool update)
561 {
562         if (update_timeout) {
563                 ecore_timer_del(update_timeout);
564                 update_timeout = NULL;
565         }
566
567         if (update) {
568                 update_timeout = ecore_timer_add(AUTOMATIC_DELAY_TIME,
569                     update_handler, NULL);
570         }
571 }
572
573 static int prepare_lsensor(void *data)
574 {
575         int status, ret;
576         int sf_state = 0;
577         int brt = -1;
578
579         ret = vconf_get_int(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT, &status);
580
581         if (ret == 0 && status == SETTING_BRIGHTNESS_AUTOMATIC_ON)
582                 set_autobrightness_state(status);
583
584         /* add auto_brt_setting change handler */
585         vconf_notify_key_changed(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT,
586                                  set_alc_function, NULL);
587
588         vconf_get_int(VCONFKEY_SETAPPL_LCD_AUTOMATIC_BRIGHTNESS, &brt);
589         if (brt < PM_MIN_BRIGHTNESS || brt > PM_MAX_BRIGHTNESS) {
590                 _E("Failed to get automatic brightness!");
591         } else {
592                 automatic_brt = brt / AUTOMATIC_DEVIDE_VAL;
593                 _I("automatic brt init success %d", automatic_brt);
594         }
595
596         vconf_notify_key_changed(VCONFKEY_SETAPPL_LCD_AUTOMATIC_BRIGHTNESS,
597                                 set_alc_automatic_brt, NULL);
598
599         return 0;
600 }
601
602 static inline void update_brightness_direct(void)
603 {
604         int ret, status;
605
606         ret = vconf_get_int(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT, &status);
607         if (ret == 0 && status == SETTING_BRIGHTNESS_AUTOMATIC_ON)
608                 alc_update_brt(true);
609 }
610
611 static int set_autobrightness_min(int val, char *name)
612 {
613         if (!name)
614                 return -EINVAL;
615
616         if (val < PM_MIN_BRIGHTNESS || val > PM_MAX_BRIGHTNESS)
617                 return -EINVAL;
618
619         min_brightness = val;
620
621         if (min_brightness_name) {
622                 free(min_brightness_name);
623                 min_brightness_name = 0;
624         }
625         min_brightness_name = strndup(name, strlen(name));
626
627         update_brightness_direct();
628
629         _I("auto brightness min value changed! (%d, %s)",
630             min_brightness, min_brightness_name);
631
632         return 0;
633 }
634
635 static int reset_autobrightness_min(char *name, enum watch_id id)
636 {
637         if (!name)
638                 return -EINVAL;
639
640         if (!min_brightness_name)
641                 return -EINVAL;
642
643         if (strcmp(name, min_brightness_name))
644                 return -EINVAL;
645
646         _I("change to default %d -> %d, %s", min_brightness,
647             PM_MIN_BRIGHTNESS, min_brightness_name);
648         min_brightness = PM_MIN_BRIGHTNESS;
649         if (min_brightness_name) {
650                 free(min_brightness_name);
651                 min_brightness_name = 0;
652         }
653
654         update_brightness_direct();
655
656         return 0;
657 }
658
659 static void auto_brightness_init(void *data)
660 {
661         display_info.update_auto_brightness = update_auto_brightness;
662         display_info.set_autobrightness_min = set_autobrightness_min;
663         display_info.reset_autobrightness_min = reset_autobrightness_min;
664
665         prepare_lsensor(NULL);
666 }
667
668 static void auto_brightness_exit(void *data)
669 {
670         vconf_ignore_key_changed(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT,
671             set_alc_function);
672
673         vconf_ignore_key_changed(VCONFKEY_SETAPPL_LCD_AUTOMATIC_BRIGHTNESS,
674             set_alc_automatic_brt);
675
676         set_autobrightness_state(SETTING_BRIGHTNESS_AUTOMATIC_OFF);
677 }
678
679 static const struct display_ops display_autobrightness_ops = {
680         .name     = "auto-brightness",
681         .init     = auto_brightness_init,
682         .exit     = auto_brightness_exit,
683 };
684
685 DISPLAY_OPS_REGISTER(&display_autobrightness_ops)
686