4 * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd.
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
26 #include <sys/types.h>
29 #include <sensor_internal.h>
34 #include "display-ops.h"
35 #include "device-node.h"
38 #include "proc/proc-handler.h"
39 #include "core/device-notifier.h"
40 #include "core/config-parser.h"
42 #define DISP_FORCE_SHIFT 12
43 #define DISP_FORCE_CMD(prop, force) (((force) << DISP_FORCE_SHIFT) | prop)
45 #define SAMPLING_INTERVAL 1 /* 1 sec */
46 #define MAX_SAMPLING_COUNT 3
48 #define DEFAULT_AUTOMATIC_BRT 5
49 #define MAX_AUTOMATIC_COUNT 11
50 #define AUTOMATIC_DEVIDE_VAL 10
51 #define AUTOMATIC_DELAY_TIME 0.5 /* 0.5 sec */
53 #define RADIAN_VALUE (57.2957)
54 #define ROTATION_90 90
55 #define WORKING_ANGLE_MIN 0
56 #define WORKING_ANGLE_MAX 20
58 #define ISVALID_AUTOMATIC_INDEX(index) (index >= 0 && index < MAX_AUTOMATIC_COUNT)
59 #define BOARD_CONF_FILE "/etc/deviced/display.conf"
73 struct lbm_config lbm_conf = {
77 .off_count = OFF_COUNT,
80 static int (*_default_action) (int);
81 static Ecore_Timer *alc_timeout_id = 0;
82 static Ecore_Timer *update_timeout;
83 static int light_handle = -1;
84 static int accel_handle = -1;
85 static int fault_count = 0;
86 static int automatic_brt = DEFAULT_AUTOMATIC_BRT;
87 static int min_brightness = PM_MIN_BRIGHTNESS;
88 static char *min_brightness_name = 0;
89 static int value_table[MAX_AUTOMATIC_COUNT];
90 static int lbm_state = -1;
92 static bool update_working_position(void)
96 float x, y, z, pitch, realg;
98 if (!display_conf.accel_sensor_on)
101 ret = sf_get_data(accel_handle, ACCELEROMETER_BASE_DATA_SET, &data);
103 _E("Fail to get accelerometer data! %d", ret);
111 realg = (float)sqrt((x * x) + (y * y) + (z * z));
112 pitch = ROTATION_90 - abs((int) (asin(z / realg) * RADIAN_VALUE));
114 _D("accel data [%f, %f, %f] - %f", x, y, z, pitch);
116 if (pitch >= WORKING_ANGLE_MIN && pitch <= WORKING_ANGLE_MAX)
121 static int get_siop_brightness(int value)
125 cmd = DISP_CMD(PROP_DISPLAY_MAX_BRIGHTNESS, DEFAULT_DISPLAY);
126 ret = device_get_property(DEVICE_TYPE_DISPLAY, cmd, &brt);
127 if (ret >= 0 && value > brt)
133 static void alc_set_brightness(int setting, int value, int lux)
136 int position, cmd, tmp_value = 0;
138 cmd = DISP_CMD(PROP_DISPLAY_BRIGHTNESS, DEFAULT_DISPLAY);
139 if (device_get_property(DEVICE_TYPE_DISPLAY, cmd, &tmp_value) < 0) {
140 _E("Fail to get display brightness!");
144 if (value < min_brightness)
145 value = min_brightness;
147 if (cur_siop_level() != 0)
148 value = get_siop_brightness(value);
150 if (tmp_value != value) {
151 if (!setting && min_brightness == PM_MIN_BRIGHTNESS &&
152 display_conf.accel_sensor_on == true) {
153 position = update_working_position();
154 if (!position && (old > lux)) {
155 _D("It's not working position, "
156 "LCD isn't getting dark!");
162 diff = value - tmp_value;
163 if (abs(diff) < display_conf.brightness_change_step)
164 step = (diff > 0 ? 1 : -1);
166 step = (int)ceil(diff /
167 (float)display_conf.brightness_change_step);
170 while (tmp_value != value) {
171 if (step == 0) break;
174 if ((step > 0 && tmp_value > value) ||
175 (step < 0 && tmp_value < value))
178 backlight_ops.set_default_brt(tmp_value);
179 backlight_ops.update();
181 _I("load light data:%d lux,auto brt %d,min brightness %d,"
182 "brightness %d", lux, automatic_brt, min_brightness, value);
187 static bool check_lbm(int lux, int setting)
189 static int on_count, off_count;
194 if (lbm_conf.on < 0 || lbm_conf.off < 0)
197 if (lux <= lbm_conf.on && lbm_state != true) {
200 if (on_count >= lbm_conf.on_count || setting) {
206 } else if (lux >= lbm_conf.off && lbm_state != false) {
209 if (off_count >= lbm_conf.off_count || setting) {
215 } else if (lux > lbm_conf.on && lux < lbm_conf.off) {
226 static bool check_brightness_changed(int value)
229 static int values[MAX_SAMPLING_COUNT], count = 0;
231 if (!get_hallic_open())
234 if (count >= MAX_SAMPLING_COUNT || count < 0)
237 values[count++] = value;
239 for (i = 0; i < MAX_SAMPLING_COUNT - 1; i++)
240 if (values[i] != values[i+1])
245 static bool check_hbm(int lux)
247 int ret, old_state, new_state;
248 static int on_count, off_count;
250 if (hbm_get_state == NULL)
253 old_state = hbm_get_state();
255 _E("Failed to get HBM state!");
260 if (lux <= hbm_conf.off)
264 new_state = (off_count >= hbm_conf.off_count ? 0 : 1);
266 if (lux >= hbm_conf.on)
270 new_state = (on_count >= hbm_conf.on_count ? 1 : 0);
273 if (old_state != new_state) {
274 on_count = off_count = 0;
275 _D("hbm is %s", (new_state ? "on" : "off"));
276 ret = hbm_set_state(new_state);
278 _E("Failed to set HBM state!");
280 * Brightness is changed directly
281 * when hbm state is changed from on to off
284 display_info.update_auto_brightness(true);
287 return (new_state ? true : false);
290 static bool alc_update_brt(bool setting)
296 sensor_data_t light_data;
298 ret = sf_get_data(light_handle, LIGHT_LUX_DATA_SET, &light_data);
299 if (ret < 0 || (int)light_data.values[0] < 0) {
302 int force = (setting ? 1 : 0);
303 cmd = DISP_FORCE_CMD(PROP_DISPLAY_BRIGHTNESS_BY_LUX, force);
304 cmd = DISP_CMD(cmd, (int)light_data.values[0]);
305 ret = device_get_property(DEVICE_TYPE_DISPLAY, cmd, value_table);
307 value = (ISVALID_AUTOMATIC_INDEX(automatic_brt) ?
308 value_table[automatic_brt] : value_table[DEFAULT_AUTOMATIC_BRT]);
310 if (ret < 0 || value < PM_MIN_BRIGHTNESS ||
311 value > PM_MAX_BRIGHTNESS) {
312 _E("fail to load light data : %d lux, %d",
313 (int)light_data.values[0], value);
318 /* Check HBM (High Brightness Mode) */
319 if (check_hbm((int)light_data.values[0]))
322 if (!check_lbm((int)light_data.values[0], setting))
325 if (display_conf.continuous_sampling &&
326 !check_brightness_changed(value) && !setting)
329 alc_set_brightness(setting, value, (int)light_data.values[0]);
333 if ((fault_count > MAX_FAULT) && !(pm_status_flag & PWROFF_FLAG)) {
334 if (alc_timeout_id > 0)
335 ecore_timer_del(alc_timeout_id);
336 alc_timeout_id = NULL;
337 vconf_set_int(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT,
338 SETTING_BRIGHTNESS_AUTOMATIC_OFF);
339 _E("Fault counts is over %d, disable automatic brightness",
346 static bool alc_handler(void* data)
348 if (pm_cur_state != S_NORMAL || !get_hallic_open()){
349 if (alc_timeout_id > 0)
350 ecore_timer_del(alc_timeout_id);
351 alc_timeout_id = NULL;
355 if (alc_update_brt(false) == EINA_FALSE)
358 if (alc_timeout_id != 0)
364 static int alc_action(int timeout)
366 /* sampling timer add */
367 if (alc_timeout_id == 0 && !(pm_status_flag & PWRSV_FLAG)) {
368 display_info.update_auto_brightness(true);
371 ecore_timer_add(display_conf.lightsensor_interval,
372 (Ecore_Task_Cb)alc_handler, NULL);
375 if (_default_action != NULL)
376 return _default_action(timeout);
378 /* unreachable code */
382 static int connect_sfsvc(void)
386 _I("connect with sensor fw");
388 light_handle = sf_connect(LIGHT_SENSOR);
389 if (light_handle < 0) {
390 _E("light sensor attach fail");
393 sf_state = sf_start(light_handle, 0);
395 _E("light sensor attach fail");
396 sf_disconnect(light_handle);
400 sf_change_sensor_option(light_handle, 1);
402 if (!display_conf.accel_sensor_on)
405 /* accelerometer sensor */
406 accel_handle = sf_connect(ACCELEROMETER_SENSOR);
407 if (accel_handle < 0) {
408 _E("accelerometer sensor attach fail");
411 sf_state = sf_start(accel_handle, 0);
413 _E("accelerometer sensor attach fail");
414 sf_disconnect(accel_handle);
418 sf_change_sensor_option(accel_handle, 1);
425 if (light_handle >= 0) {
426 sf_stop(light_handle);
427 sf_disconnect(light_handle);
430 if (display_conf.accel_sensor_on && accel_handle >= 0) {
431 sf_stop(accel_handle);
432 sf_disconnect(accel_handle);
438 static int disconnect_sfsvc(void)
440 _I("disconnect with sensor fw");
442 if(light_handle >= 0) {
443 sf_stop(light_handle);
444 sf_disconnect(light_handle);
447 /* accelerometer sensor*/
448 if (display_conf.accel_sensor_on && accel_handle >= 0) {
449 sf_stop(accel_handle);
450 sf_disconnect(accel_handle);
454 if (_default_action != NULL) {
455 states[S_NORMAL].action = _default_action;
456 _default_action = NULL;
458 if (alc_timeout_id > 0) {
459 ecore_timer_del(alc_timeout_id);
460 alc_timeout_id = NULL;
466 static inline void set_brtch_state(void)
468 if (pm_status_flag & PWRSV_FLAG) {
469 pm_status_flag |= BRTCH_FLAG;
470 vconf_set_bool(VCONFKEY_PM_BRIGHTNESS_CHANGED_IN_LPM, true);
471 _D("brightness changed in low battery,"
472 "escape dim state (light)");
476 static int set_autobrightness_state(int status)
480 int default_brt = -1;
483 if (status == SETTING_BRIGHTNESS_AUTOMATIC_ON) {
484 if(connect_sfsvc() < 0)
487 /* escape dim state if it's in low battery.*/
490 /* change alc action func */
491 if (_default_action == NULL)
492 _default_action = states[S_NORMAL].action;
493 states[S_NORMAL].action = alc_action;
495 display_info.update_auto_brightness(true);
498 ecore_timer_add(display_conf.lightsensor_interval,
499 (Ecore_Task_Cb)alc_handler, NULL);
500 } else if (status == SETTING_BRIGHTNESS_AUTOMATIC_PAUSE) {
501 _I("auto brightness paused!");
503 backlight_ops.hbm_off();
507 backlight_ops.hbm_off();
509 /* escape dim state if it's in low battery.*/
512 ret = get_setting_brightness(&default_brt);
513 if (ret != 0 || (default_brt < PM_MIN_BRIGHTNESS || default_brt > PM_MAX_BRIGHTNESS)) {
514 _I("fail to read vconf value for brightness");
515 brt = PM_DEFAULT_BRIGHTNESS;
516 if(default_brt < PM_MIN_BRIGHTNESS || default_brt > PM_MAX_BRIGHTNESS)
517 vconf_set_int(VCONFKEY_SETAPPL_LCD_BRIGHTNESS, brt);
521 backlight_ops.set_default_brt(default_brt);
522 backlight_ops.update();
528 static void set_alc_function(keynode_t *key_nodes, void *data)
532 if (key_nodes == NULL) {
533 _E("wrong parameter, key_nodes is null");
537 status = vconf_keynode_get_int(key_nodes);
540 case SETTING_BRIGHTNESS_AUTOMATIC_OFF:
541 case SETTING_BRIGHTNESS_AUTOMATIC_ON:
542 case SETTING_BRIGHTNESS_AUTOMATIC_PAUSE:
543 ret = set_autobrightness_state(status);
544 _D("set auto brightness : %d", ret);
547 _E("invalid value! %d", status);
551 static bool check_sfsvc(void* data)
553 /* this function will return opposite value for re-callback in fail */
557 _I("register sfsvc");
559 vconf_get_int(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT, &vconf_auto);
560 if (vconf_auto == SETTING_BRIGHTNESS_AUTOMATIC_ON) {
561 if(connect_sfsvc() < 0)
564 /* change alc action func */
565 if (_default_action == NULL)
566 _default_action = states[S_NORMAL].action;
567 states[S_NORMAL].action = alc_action;
569 ecore_timer_add(display_conf.lightsensor_interval,
570 (Ecore_Task_Cb)alc_handler, NULL);
571 if (alc_timeout_id > 0)
576 _I("change vconf value before registering sfsvc");
580 static void set_alc_automatic_brt(keynode_t *key_nodes, void *data)
582 if (key_nodes == NULL) {
583 _E("wrong parameter, key_nodes is null");
586 automatic_brt = vconf_keynode_get_int(key_nodes) / AUTOMATIC_DEVIDE_VAL;
587 _D("automatic brt : %d", automatic_brt);
589 alc_update_brt(true);
592 static Eina_Bool update_handler(void* data)
596 update_timeout = NULL;
598 if (pm_cur_state != S_NORMAL)
601 if (!get_hallic_open())
604 ret = vconf_get_int(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT, &on);
605 if (ret < 0 || on != SETTING_BRIGHTNESS_AUTOMATIC_ON)
608 _D("auto brightness is working!");
609 alc_update_brt(true);
614 static void update_auto_brightness(bool update)
616 if (update_timeout) {
617 ecore_timer_del(update_timeout);
618 update_timeout = NULL;
622 update_timeout = ecore_timer_add(AUTOMATIC_DELAY_TIME,
623 update_handler, NULL);
627 static int prepare_lsensor(void *data)
633 ret = vconf_get_int(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT, &status);
635 if (ret == 0 && status == SETTING_BRIGHTNESS_AUTOMATIC_ON)
636 set_autobrightness_state(status);
638 /* add auto_brt_setting change handler */
639 vconf_notify_key_changed(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT,
640 set_alc_function, NULL);
642 vconf_get_int(VCONFKEY_SETAPPL_LCD_AUTOMATIC_BRIGHTNESS, &brt);
643 if (brt < PM_MIN_BRIGHTNESS || brt > PM_MAX_BRIGHTNESS) {
644 _E("Failed to get automatic brightness!");
646 automatic_brt = brt / AUTOMATIC_DEVIDE_VAL;
647 _I("automatic brt init success %d", automatic_brt);
650 vconf_notify_key_changed(VCONFKEY_SETAPPL_LCD_AUTOMATIC_BRIGHTNESS,
651 set_alc_automatic_brt, NULL);
656 static void update_brightness_direct(void)
660 ret = vconf_get_int(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT, &status);
661 if (ret == 0 && status == SETTING_BRIGHTNESS_AUTOMATIC_ON)
662 alc_update_brt(true);
665 static int set_autobrightness_min(int val, char *name)
670 if (val < PM_MIN_BRIGHTNESS || val > PM_MAX_BRIGHTNESS)
673 min_brightness = val;
675 if (min_brightness_name) {
676 free(min_brightness_name);
677 min_brightness_name = 0;
679 min_brightness_name = strndup(name, strlen(name));
681 update_brightness_direct();
683 _I("auto brightness min value changed! (%d, %s)",
684 min_brightness, min_brightness_name);
689 static int reset_autobrightness_min(char *name, enum watch_id id)
694 if (!min_brightness_name)
697 if (strcmp(name, min_brightness_name))
700 _I("change to default %d -> %d, %s", min_brightness,
701 PM_MIN_BRIGHTNESS, min_brightness_name);
702 min_brightness = PM_MIN_BRIGHTNESS;
703 if (min_brightness_name) {
704 free(min_brightness_name);
705 min_brightness_name = 0;
708 update_brightness_direct();
713 static int lcd_changed_cb(void *data)
715 int lcd_state = (int)data;
717 if (lcd_state == S_LCDOFF && alc_timeout_id > 0) {
718 ecore_timer_del(alc_timeout_id);
719 alc_timeout_id = NULL;
725 static int lbm_load_config(struct parse_result *result, void *user_data)
727 struct lbm_config *c = user_data;
729 _D("%s,%s,%s", result->section, result->name, result->value);
734 if (!MATCH(result->section, "LBM"))
737 if (MATCH(result->name, "on")) {
738 SET_CONF(c->on, atoi(result->value));
739 _D("on lux is %d", c->on);
740 } else if (MATCH(result->name, "off")) {
741 SET_CONF(c->off, atoi(result->value));
742 _D("off lux is %d", c->off);
743 } else if (MATCH(result->name, "on_count")) {
744 SET_CONF(c->on_count, atoi(result->value));
745 _D("on count is %d", c->on_count);
746 } else if (MATCH(result->name, "off_count")) {
747 SET_CONF(c->off_count, atoi(result->value));
748 _D("off count is %d", c->off_count);
754 static void auto_brightness_init(void *data)
758 display_info.update_auto_brightness = update_auto_brightness;
759 display_info.set_autobrightness_min = set_autobrightness_min;
760 display_info.reset_autobrightness_min = reset_autobrightness_min;
762 /* load configutation */
763 ret = config_parse(BOARD_CONF_FILE, lbm_load_config, &lbm_conf);
765 _W("Failed to load %s, %s Use default value!",
766 BOARD_CONF_FILE, ret);
768 register_notifier(DEVICE_NOTIFIER_LCD, lcd_changed_cb);
770 prepare_lsensor(NULL);
773 static void auto_brightness_exit(void *data)
775 vconf_ignore_key_changed(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT,
778 vconf_ignore_key_changed(VCONFKEY_SETAPPL_LCD_AUTOMATIC_BRIGHTNESS,
779 set_alc_automatic_brt);
781 unregister_notifier(DEVICE_NOTIFIER_LCD, lcd_changed_cb);
783 set_autobrightness_state(SETTING_BRIGHTNESS_AUTOMATIC_OFF);
786 static const struct display_ops display_autobrightness_ops = {
787 .name = "auto-brightness",
788 .init = auto_brightness_init,
789 .exit = auto_brightness_exit,
792 DISPLAY_OPS_REGISTER(&display_autobrightness_ops)