display: Change get_var_display_config return type to const
[platform/core/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
31
32 #include "util.h"
33 #include "core.h"
34 #include "display-ops.h"
35 #include "setting.h"
36 #include "shared/device-notifier.h"
37 #include <libsyscommon/ini-parser.h>
38
39 #define METHOD_CHECK_SUPPORTED  "CheckSupported"
40
41 #define DISP_FORCE_SHIFT        12
42 #define DISP_FORCE_CMD(prop, force)     (((force) << DISP_FORCE_SHIFT) | prop)
43
44 #define SAMPLING_INTERVAL       1       /* 1 sec */
45 #define MAX_SAMPLING_COUNT      3
46 #define MAX_FAULT               5
47 #define DEFAULT_AUTOMATIC_BRT   5
48 #define AUTOMATIC_DEVIDE_VAL    10
49 #define AUTOMATIC_DELAY_TIME    500     /* 0.5 sec */
50
51 #define RADIAN_VALUE            (57.2957)
52 #define ROTATION_90             90
53 #define WORKING_ANGLE_MIN       0
54 #define WORKING_ANGLE_MAX       20
55
56 #define BOARD_CONF_FILE "/etc/deviced/display.conf"
57
58 #define ON_LUX          -1
59 #define OFF_LUX         -1
60 #define ON_COUNT        1
61 #define OFF_COUNT       1
62
63 static struct _backlight_ops *backlight_ops;
64 static int (*_default_action) (int);
65 static guint alc_timeout_id = 0;
66 static guint update_timeout;
67 static sensor_listener_h light_listener;
68 static sensor_listener_h accel_listener;
69 static int fault_count;
70 static int automatic_brt = DEFAULT_AUTOMATIC_BRT;
71 static int min_brightness = PM_MIN_BRIGHTNESS;
72 static char *min_brightness_name = 0;
73
74 /* light sensor */
75 static float lmax, lmin;
76 static const struct display_config *display_conf;
77
78 static bool update_working_position(void)
79 {
80         sensor_event_s data;
81         int ret;
82         float x, y, z, pitch, realg;
83
84         if (!display_conf->accel_sensor_on)
85                 return false;
86
87         ret = sensor_listener_read_data(accel_listener, &data);
88         if (ret != SENSOR_ERROR_NONE) {
89                 _E("Failed to get accelerometer data: %d", ret);
90                 return true;
91         }
92
93         x = data.values[0];
94         y = data.values[1];
95         z = data.values[2];
96
97         realg = (float)sqrt((x * x) + (y * y) + (z * z));
98         pitch = ROTATION_90 - abs((int) (asin(z / realg) * RADIAN_VALUE));
99
100         _D("Accel data x=%f y=%f z=%f pitch=%f", x, y, z, pitch);
101
102         if (pitch >= WORKING_ANGLE_MIN && pitch <= WORKING_ANGLE_MAX)
103                 return true;
104         return false;
105 }
106
107 static void alc_set_brightness(int setting, int value, float light)
108 {
109         static float old;
110         int position, tmp_value = 0, ret;
111
112         ret = backlight_ops->get_brightness(&tmp_value);
113         if (ret < 0) {
114                 _E("Failed to get display brightness.");
115                 return;
116         }
117
118         if (value < min_brightness)
119                 value = min_brightness;
120
121         if (tmp_value != value) {
122                 if (!setting && min_brightness == PM_MIN_BRIGHTNESS &&
123                     display_conf->accel_sensor_on == true) {
124                         position = update_working_position();
125                         if (!position && (old > light)) {
126                                 _D("It's not working position, "
127                                     "LCD isn't getting dark.");
128                                 return;
129                         }
130                 }
131                 int diff, step;
132
133                 diff = value - tmp_value;
134                 if (abs(diff) < display_conf->brightness_change_step)
135                         step = (diff > 0 ? 1 : -1);
136                 else
137                         step = (int)ceil(diff /
138                             (float)display_conf->brightness_change_step);
139
140                 _D("%d", step);
141                 while (tmp_value != value) {
142                         if (step == 0) break;
143
144                         tmp_value += step;
145                         if ((step > 0 && tmp_value > value) ||
146                             (step < 0 && tmp_value < value))
147                                 tmp_value = value;
148
149                         backlight_ops->set_default_brt(tmp_value);
150                         backlight_ops->update();
151                 }
152                 _I("Load light data(%f) auto brt=%d min brightness=%d "
153                     "brightness=%d", light, automatic_brt, min_brightness, value);
154                 old = light;
155         }
156 }
157
158 static bool check_brightness_changed(int value)
159 {
160         int i;
161         static int values[MAX_SAMPLING_COUNT], count = 0;
162
163         if (count >= MAX_SAMPLING_COUNT || count < 0)
164                 count = 0;
165
166         values[count++] = value;
167
168         for (i = 0; i < MAX_SAMPLING_COUNT - 1; i++)
169                 if (values[i] != values[i+1])
170                         return false;
171         return true;
172 }
173
174 static bool alc_update_brt(bool setting)
175 {
176         int value = 0;
177         int ret = -1;
178         sensor_event_s light_data;
179         int index;
180         float light;
181
182         ret = sensor_listener_read_data(light_listener, &light_data);
183         if (ret != SENSOR_ERROR_NONE) {
184                 _E("Failed to read light sensor data: %d", ret);
185                 goto out;
186         }
187
188         index = light_data.value_count - 1;
189         if (index < 0 || light_data.values[index] < 0) {
190                 _E("Invalid light sensor data.");
191                 goto out;
192         }
193
194         light = light_data.values[index];
195         ret = backlight_ops->get_brightness_by_light_sensor(
196                         lmax, lmin, light, &value);
197         if (ret == -ENOTSUP) {
198                 _E("Not supported to handle the light data.");
199                 goto out;
200         }
201
202         if (ret < 0 || value < PM_MIN_BRIGHTNESS || value > PM_MAX_BRIGHTNESS) {
203                 _E("Failed to load light data. light=%f value=%d: %d", light, value, ret);
204                 goto out;
205         }
206
207         fault_count = 0;
208
209         if (display_conf->continuous_sampling &&
210                 !check_brightness_changed(value) &&
211                 !setting)
212                 return true;
213
214         alc_set_brightness(setting, value, light);
215
216         return true;
217
218 out:
219         fault_count++;
220
221         if ((fault_count > MAX_FAULT) && !(get_pm_status_flag() & PWROFF_FLAG)) {
222                 if (alc_timeout_id) {
223                         g_source_remove(alc_timeout_id);
224                         alc_timeout_id = 0;
225                 }
226                 ret = vconf_set_int(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT,
227                     SETTING_BRIGHTNESS_AUTOMATIC_OFF);
228                 if (ret < 0)
229                         _E("Failed to set vconf value for automatic brightness: %d", vconf_get_ext_errno());
230                 _E("Fault counts is over %d, disable automatic brightness.", MAX_FAULT);
231                 return false;
232         }
233         return true;
234 }
235
236 static gboolean alc_handler(void *data)
237 {
238         if (get_pm_cur_state() != S_NORMAL) {
239                 if (alc_timeout_id > 0)
240                         g_source_remove(alc_timeout_id);
241                 alc_timeout_id = 0;
242                 return G_SOURCE_REMOVE;
243         }
244
245         if (!alc_update_brt(false))
246                 return G_SOURCE_REMOVE;
247
248         if (alc_timeout_id != 0)
249                 return G_SOURCE_CONTINUE;
250
251         return G_SOURCE_REMOVE;
252 }
253
254 static int alc_action(int timeout)
255 {
256         /* sampling timer add */
257         if (alc_timeout_id == 0 && !(get_pm_status_flag() & PWRSV_FLAG)) {
258                 display_info.update_auto_brightness(true);
259
260                 alc_timeout_id =
261                     g_timeout_add_seconds(display_conf->lightsensor_interval,
262                             alc_handler, NULL);
263         }
264
265         if (_default_action != NULL)
266                 return _default_action(timeout);
267
268         /* unreachable code */
269         return -1;
270 }
271
272 static int connect_sensor(void)
273 {
274         int ret;
275         sensor_h sensor;
276         sensor_h *list = NULL;
277         int cnt = 0;
278
279         _I("Connect with sensor fw.");
280         /* light sensor */
281         ret = sensor_get_sensor_list(SENSOR_LIGHT, &list, &cnt);
282         if (ret != SENSOR_ERROR_NONE) {
283                 _E("Failed to get light sensor list: %d", ret);
284                 goto error;
285         }
286
287         /* TODO
288          * Sensor apis will be fixed
289          * to provide which sensor is effective among sensors */
290         if (cnt == 3) /* three light sensors exist */
291                 sensor = list[2];
292         else
293                 sensor = list[0];
294
295         g_free(list);
296
297         ret = sensor_get_min_range(sensor, &lmin);
298         if (ret != SENSOR_ERROR_NONE) {
299                 _E("Failed to get light sensor min range: %d", ret);
300                 goto error;
301         }
302         ret = sensor_get_max_range(sensor, &lmax);
303         if (ret != SENSOR_ERROR_NONE) {
304                 _E("Failed to get light sensor max range: %d", ret);
305                 goto error;
306         }
307         _I("Light sensor min=%f max=%f", lmin, lmax);
308
309         ret = sensor_create_listener(sensor, &light_listener);
310         if (ret != SENSOR_ERROR_NONE) {
311                 _E("Failed to create listener(light).");
312                 goto error;
313         }
314         sensor_listener_set_option(light_listener, SENSOR_OPTION_ALWAYS_ON);
315         ret = sensor_listener_start(light_listener);
316         if (ret != SENSOR_ERROR_NONE) {
317                 _E("Failed to start light sensor.");
318                 sensor_destroy_listener(light_listener);
319                 light_listener = 0;
320                 goto error;
321         }
322
323         if (!display_conf->accel_sensor_on)
324                 goto success;
325
326         /* accelerometer sensor */
327         ret = sensor_get_default_sensor(SENSOR_ACCELEROMETER, &sensor);
328         if (ret != SENSOR_ERROR_NONE) {
329                 _E("Failed to get default accel sensor.");
330                 goto error;
331         }
332         ret = sensor_create_listener(&sensor, &accel_listener);
333         if (ret != SENSOR_ERROR_NONE) {
334                 _E("Failed to create listener(accel).");
335                 goto error;
336         }
337         sensor_listener_set_option(accel_listener, SENSOR_OPTION_ALWAYS_ON);
338         ret = sensor_listener_start(accel_listener);
339         if (ret != SENSOR_ERROR_NONE) {
340                 _E("Failed to start accel sensor.");
341                 sensor_destroy_listener(accel_listener);
342                 accel_listener = 0;
343                 goto error;
344         }
345
346 success:
347         fault_count = 0;
348         return 0;
349
350 error:
351         if (light_listener > 0) {
352                 sensor_listener_stop(light_listener);
353                 sensor_destroy_listener(light_listener);
354                 light_listener = 0;
355         }
356         if (display_conf->accel_sensor_on && accel_listener > 0) {
357                 sensor_listener_stop(accel_listener);
358                 sensor_destroy_listener(accel_listener);
359                 accel_listener = 0;
360         }
361         return -EIO;
362 }
363
364 static int disconnect_sensor(void)
365 {
366         _I("Disconnect with sensor fw.");
367         /* light sensor*/
368         if (light_listener > 0) {
369                 sensor_listener_stop(light_listener);
370                 sensor_destroy_listener(light_listener);
371                 light_listener = 0;
372         }
373
374         /* accelerometer sensor*/
375         if (display_conf->accel_sensor_on && accel_listener > 0) {
376                 sensor_listener_stop(accel_listener);
377                 sensor_destroy_listener(accel_listener);
378                 accel_listener = 0;
379         }
380
381         if (_default_action != NULL) {
382                 state_st(S_NORMAL)->action = _default_action;
383                 _default_action = NULL;
384         }
385         if (alc_timeout_id > 0) {
386                 g_source_remove(alc_timeout_id);
387                 alc_timeout_id = 0;
388         }
389
390         return 0;
391 }
392
393 void set_brightness_changed_state(void)
394 {
395         if (get_pm_status_flag() & PWRSV_FLAG) {
396                 set_pm_status_flag(BRTCH_FLAG);
397                 _D("Brightness changed in low battery,"
398                     "escape dim state (light).");
399         }
400 }
401
402 static int set_autobrightness_state(int status)
403 {
404         int ret = -1;
405         int brt = -1;
406         int default_brt = -1;
407
408         if (status == SETTING_BRIGHTNESS_AUTOMATIC_ON) {
409                 if (connect_sensor() < 0)
410                         return -1;
411
412                 /* escape dim state if it's in low battery.*/
413                 set_brightness_changed_state();
414
415                 /* change alc action func */
416                 if (_default_action == NULL)
417                         _default_action = state_st(S_NORMAL)->action;
418                 state_st(S_NORMAL)->action = alc_action;
419
420                 display_info.update_auto_brightness(true);
421
422                 alc_timeout_id =
423                     g_timeout_add_seconds(display_conf->lightsensor_interval,
424                             alc_handler, NULL);
425         } else if (status == SETTING_BRIGHTNESS_AUTOMATIC_PAUSE) {
426                 _I("Auto brightness paused.");
427                 disconnect_sensor();
428         } else {
429                 disconnect_sensor();
430                 /* escape dim state if it's in low battery.*/
431                 set_brightness_changed_state();
432
433                 ret = get_setting_brightness(&default_brt);
434                 if (ret != 0 || (default_brt < PM_MIN_BRIGHTNESS || default_brt > PM_MAX_BRIGHTNESS)) {
435                         _I("Failed to read vconf value for brightness.");
436                         brt = display_conf->pm_default_brightness;
437                         if (default_brt < PM_MIN_BRIGHTNESS || default_brt > PM_MAX_BRIGHTNESS) {
438                                 ret = vconf_set_int(VCONFKEY_SETAPPL_LCD_BRIGHTNESS, brt);
439                                 if (ret < 0)
440                                         _E("Failed to set vconf value for lcd brightness: %d", vconf_get_ext_errno());
441                         }
442                         default_brt = brt;
443                 }
444
445                 backlight_ops->set_default_brt(default_brt);
446                 backlight_ops->update();
447         }
448
449         return 0;
450 }
451
452 static void set_alc_function(keynode_t *key_nodes, void *data)
453 {
454         int status, ret;
455
456         if (key_nodes == NULL) {
457                 _E("Wrong parameter, key_nodes is null.");
458                 return;
459         }
460
461         status = vconf_keynode_get_int(key_nodes);
462
463         switch (status) {
464         case SETTING_BRIGHTNESS_AUTOMATIC_OFF:
465         case SETTING_BRIGHTNESS_AUTOMATIC_ON:
466         case SETTING_BRIGHTNESS_AUTOMATIC_PAUSE:
467                 ret = set_autobrightness_state(status);
468                 _D("Set auto brightness: %d", ret);
469                 break;
470         default:
471                 _E("Invalid value(%d)", status);
472         }
473 }
474
475 static void set_alc_automatic_brt(keynode_t *key_nodes, void *data)
476 {
477         if (key_nodes == NULL) {
478                 _E("Wrong parameter, key_nodes is null.");
479                 return;
480         }
481         automatic_brt = vconf_keynode_get_int(key_nodes) / AUTOMATIC_DEVIDE_VAL;
482         _D("Automatic brt(%d)", automatic_brt);
483
484         alc_update_brt(true);
485 }
486
487 static gboolean update_handler(void *data)
488 {
489         int ret, on;
490
491         update_timeout = 0;
492
493         if (get_pm_cur_state() != S_NORMAL)
494                 return G_SOURCE_REMOVE;
495
496         ret = vconf_get_int(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT, &on);
497         if (ret < 0 || on != SETTING_BRIGHTNESS_AUTOMATIC_ON) {
498                 if (ret < 0)
499                         _E("Failed to get vconf value for automatic brightness: %d", vconf_get_ext_errno());
500                 return G_SOURCE_REMOVE;
501         }
502
503         _D("Auto brightness is working.");
504         alc_update_brt(true);
505
506         return G_SOURCE_REMOVE;
507 }
508
509 static void update_auto_brightness(bool update)
510 {
511         if (update_timeout) {
512                 g_source_remove(update_timeout);
513                 update_timeout = 0;
514         }
515
516         if (update) {
517                 update_timeout = g_timeout_add(AUTOMATIC_DELAY_TIME,
518                     update_handler, NULL);
519         }
520 }
521
522 static int prepare_lsensor(void *data)
523 {
524         int status, ret;
525         int brt;
526
527         ret = vconf_get_int(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT, &status);
528
529         if (ret == 0 && status == SETTING_BRIGHTNESS_AUTOMATIC_ON)
530                 set_autobrightness_state(status);
531         else if (ret < 0)
532                 _E("Failed to get vconf value for automatic brightness: %d", vconf_get_ext_errno());
533
534         /* add auto_brt_setting change handler */
535         vconf_notify_key_changed(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT,
536                                  set_alc_function, NULL);
537
538         ret = vconf_get_int(VCONFKEY_SETAPPL_LCD_AUTOMATIC_BRIGHTNESS, &brt);
539         if (ret < 0) {
540                 brt = -1;
541                 _E("Failed to get vconf value for automatic lcd brightness: %d", vconf_get_ext_errno());
542         }
543         if (brt < PM_MIN_BRIGHTNESS || brt > PM_MAX_BRIGHTNESS) {
544                 _E("Failed to get automatic brightness.");
545         } else {
546                 automatic_brt = brt / AUTOMATIC_DEVIDE_VAL;
547                 _I("Automatic brt(%d) init success.", automatic_brt);
548         }
549
550         vconf_notify_key_changed(VCONFKEY_SETAPPL_LCD_AUTOMATIC_BRIGHTNESS,
551                                 set_alc_automatic_brt, NULL);
552
553         return 0;
554 }
555
556 static void update_brightness_direct(void)
557 {
558         int ret, status;
559
560         ret = vconf_get_int(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT, &status);
561         if (ret == 0 && status == SETTING_BRIGHTNESS_AUTOMATIC_ON)
562                 alc_update_brt(true);
563         else if (ret < 0)
564                 _E("Failed to get vconf value for automatic brightness: %d", vconf_get_ext_errno());
565 }
566
567 static int set_autobrightness_min(int val, char *name)
568 {
569         if (!name)
570                 return -EINVAL;
571
572         if (val < PM_MIN_BRIGHTNESS || val > PM_MAX_BRIGHTNESS)
573                 return -EINVAL;
574
575         min_brightness = val;
576
577         if (min_brightness_name) {
578                 free(min_brightness_name);
579                 min_brightness_name = 0;
580         }
581         min_brightness_name = strndup(name, strlen(name));
582
583         update_brightness_direct();
584
585         _I("Auto brightness min value changed. min_brightness=%d min_brightness_name=%s",
586             min_brightness, min_brightness_name);
587
588         return 0;
589 }
590
591 static void reset_autobrightness_min(GDBusConnection *conn,
592         const gchar     *sender,
593         const gchar     *unique_name,
594         gpointer         data)
595 {
596         if (!sender)
597                 return;
598
599         if (!min_brightness_name)
600                 return;
601
602         if (strcmp(sender, min_brightness_name))
603                 return;
604
605         _I("Change to default min brightness. before=%d changed=%d brightness_name=%s", min_brightness,
606             PM_MIN_BRIGHTNESS, min_brightness_name);
607         min_brightness = PM_MIN_BRIGHTNESS;
608         if (min_brightness_name) {
609                 free(min_brightness_name);
610                 min_brightness_name = 0;
611         }
612
613         update_brightness_direct();
614 }
615
616 static int lcd_changed_cb(void *data)
617 {
618         int lcd_state;
619
620         if (!data)
621                 return 0;
622         lcd_state = *(int *)data;
623         if (lcd_state == S_LCDOFF && alc_timeout_id > 0) {
624                 g_source_remove(alc_timeout_id);
625                 alc_timeout_id = 0;
626         }
627
628         return 0;
629 }
630
631 static int delayed_init_done(void *data)
632 {
633         int state;
634
635         if (!data)
636                 return 0;
637
638         state = *(int *)data;
639         if (state != true)
640                 return 0;
641
642         /* get light data from sensor fw */
643         prepare_lsensor(NULL);
644
645         return 0;
646 }
647
648 static void exit_lsensor(void)
649 {
650         vconf_ignore_key_changed(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT,
651             set_alc_function);
652
653         vconf_ignore_key_changed(VCONFKEY_SETAPPL_LCD_AUTOMATIC_BRIGHTNESS,
654             set_alc_automatic_brt);
655
656         set_autobrightness_state(SETTING_BRIGHTNESS_AUTOMATIC_OFF);
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         register_notifier(DEVICE_NOTIFIER_LCD, lcd_changed_cb);
666         register_notifier(DEVICE_NOTIFIER_DELAYED_INIT, delayed_init_done);
667 }
668
669 static void auto_brightness_exit(void *data)
670 {
671         exit_lsensor();
672
673         unregister_notifier(DEVICE_NOTIFIER_LCD, lcd_changed_cb);
674         unregister_notifier(DEVICE_NOTIFIER_DELAYED_INIT, delayed_init_done);
675 }
676
677 static const struct display_ops display_autobrightness_ops = {
678         .name     = "auto-brightness",
679         .init     = auto_brightness_init,
680         .exit     = auto_brightness_exit,
681 };
682
683 DISPLAY_OPS_REGISTER(&display_autobrightness_ops)
684
685 static void __CONSTRUCTOR__ initialize(void)
686 {
687         backlight_ops = get_var_backlight_ops();
688         if (!backlight_ops)
689                 _E("Failed to get backlight operator variable.");
690
691         display_conf = get_var_display_config();
692         if (!display_conf)
693                 _E("Failed to get display configuration variable.");
694 }