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.
27 #include <sys/types.h>
33 #include "device-interface.h"
34 #include "display-info.h"
35 #include "display-ops.h"
36 #include "display-backlight.h"
37 #include "display-config.h"
39 #include "shared/device-notifier.h"
40 #include "shared/log.h"
41 #include <libsyscommon/ini-parser.h>
43 #define MAX_SAMPLING_COUNT 3
45 #define DEFAULT_AUTOMATIC_BRT 5
46 #define AUTOMATIC_DEVIDE_VAL 10
47 #define AUTOMATIC_DELAY_TIME 500 /* 0.5 sec */
49 #define RADIAN_VALUE (57.2957)
50 #define ROTATION_90 90
51 #define WORKING_ANGLE_MIN 0
52 #define WORKING_ANGLE_MAX 20
54 static int (*_default_action) (int);
55 static guint alc_timeout_id = 0;
56 static guint update_timeout;
57 static sensor_listener_h light_listener;
58 static sensor_listener_h accel_listener;
59 static int fault_count;
60 static int automatic_brt = DEFAULT_AUTOMATIC_BRT;
61 static int min_brightness = PM_MIN_BRIGHTNESS;
62 static char *min_brightness_name = 0;
65 static float lmax, lmin;
67 static bool update_working_position(void)
71 float x, y, z, pitch, realg;
73 assert(g_display_plugin.config);
75 if (!g_display_plugin.config->accel_sensor_on)
78 ret = sensor_listener_read_data(accel_listener, &data);
79 if (ret != SENSOR_ERROR_NONE) {
80 _E("Failed to get accelerometer data: %d", ret);
88 realg = (float)sqrt((x * x) + (y * y) + (z * z));
89 pitch = ROTATION_90 - abs((int) (asin(z / realg) * RADIAN_VALUE));
91 _D("Accel data x=%f y=%f z=%f pitch=%f", x, y, z, pitch);
93 if (pitch >= WORKING_ANGLE_MIN && pitch <= WORKING_ANGLE_MAX)
98 static void alc_set_brightness(int setting, int value, float light)
101 int position, tmp_value = 0, ret;
103 assert(g_display_plugin.config);
105 ret = display_backlight_get_brightness(&tmp_value);
107 _E("Failed to get display brightness.");
111 if (value < min_brightness)
112 value = min_brightness;
114 if (tmp_value != value) {
115 if (!setting && min_brightness == PM_MIN_BRIGHTNESS &&
116 g_display_plugin.config->accel_sensor_on == true) {
117 position = update_working_position();
118 if (!position && (old > light)) {
119 _D("It's not working position, "
120 "LCD isn't getting dark.");
126 diff = value - tmp_value;
127 if (abs(diff) < g_display_plugin.config->brightness_change_step)
128 step = (diff > 0 ? 1 : -1);
130 step = (int)ceil(diff /
131 (float)g_display_plugin.config->brightness_change_step);
134 while (tmp_value != value) {
135 if (step == 0) break;
138 if ((step > 0 && tmp_value > value) ||
139 (step < 0 && tmp_value < value))
142 display_backlight_set_default_brightness(tmp_value);
143 display_backlight_update_by_default_brightness();
145 _I("Load light data(%f) auto brt=%d min brightness=%d "
146 "brightness=%d", light, automatic_brt, min_brightness, value);
151 static bool check_brightness_changed(int value)
154 static int values[MAX_SAMPLING_COUNT], count = 0;
156 if (count >= MAX_SAMPLING_COUNT || count < 0)
159 values[count++] = value;
161 for (i = 0; i < MAX_SAMPLING_COUNT - 1; i++)
162 if (values[i] != values[i+1])
167 static bool alc_update_brt(bool setting)
171 sensor_event_s light_data;
175 assert(g_display_plugin.config);
177 ret = sensor_listener_read_data(light_listener, &light_data);
178 if (ret != SENSOR_ERROR_NONE) {
179 _E("Failed to read light sensor data: %d", ret);
183 index = light_data.value_count - 1;
184 if (index < 0 || light_data.values[index] < 0) {
185 _E("Invalid light sensor data.");
189 light = light_data.values[index];
190 ret = display_backlight_get_brightness_by_light_sensor(
191 lmax, lmin, light, &value);
192 if (ret == -ENOTSUP) {
193 _E("Not supported to handle the light data.");
197 if (ret < 0 || value < PM_MIN_BRIGHTNESS || value > PM_MAX_BRIGHTNESS) {
198 _E("Failed to load light data. light=%f value=%d: %d", light, value, ret);
204 if (g_display_plugin.config->continuous_sampling &&
205 !check_brightness_changed(value) &&
209 alc_set_brightness(setting, value, light);
216 if ((fault_count > MAX_FAULT) && !(get_pm_status_flag() & PWROFF_FLAG)) {
217 if (alc_timeout_id) {
218 g_source_remove(alc_timeout_id);
221 ret = vconf_set_int(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT,
222 SETTING_BRIGHTNESS_AUTOMATIC_OFF);
224 _E("Failed to set vconf value for automatic brightness: %d", vconf_get_ext_errno());
225 _E("Fault counts is over %d, disable automatic brightness.", MAX_FAULT);
231 static gboolean alc_handler(void *data)
233 if (get_pm_cur_state() != DEVICED_DISPLAY_STATE_ON) {
234 if (alc_timeout_id > 0)
235 g_source_remove(alc_timeout_id);
237 return G_SOURCE_REMOVE;
240 if (!alc_update_brt(false))
241 return G_SOURCE_REMOVE;
243 if (alc_timeout_id != 0)
244 return G_SOURCE_CONTINUE;
246 return G_SOURCE_REMOVE;
249 static int alc_action(int timeout)
251 assert(g_display_plugin.config);
253 /* sampling timer add */
254 if (alc_timeout_id == 0 && !(get_pm_status_flag() & PWRSV_FLAG)) {
255 update_auto_brightness(true);
258 g_timeout_add_seconds(g_display_plugin.config->lightsensor_interval,
262 if (_default_action != NULL)
263 return _default_action(timeout);
265 /* unreachable code */
269 static int connect_sensor(void)
273 sensor_h *list = NULL;
276 assert(g_display_plugin.config);
278 _I("Connect with sensor fw.");
280 ret = sensor_get_sensor_list(SENSOR_LIGHT, &list, &cnt);
281 if (ret != SENSOR_ERROR_NONE) {
282 _E("Failed to get light sensor list: %d", ret);
287 * Sensor apis will be fixed
288 * to provide which sensor is effective among sensors */
289 if (cnt == 3) /* three light sensors exist */
296 ret = sensor_get_min_range(sensor, &lmin);
297 if (ret != SENSOR_ERROR_NONE) {
298 _E("Failed to get light sensor min range: %d", ret);
301 ret = sensor_get_max_range(sensor, &lmax);
302 if (ret != SENSOR_ERROR_NONE) {
303 _E("Failed to get light sensor max range: %d", ret);
306 _I("Light sensor min=%f max=%f", lmin, lmax);
308 ret = sensor_create_listener(sensor, &light_listener);
309 if (ret != SENSOR_ERROR_NONE) {
310 _E("Failed to create listener(light).");
313 sensor_listener_set_option(light_listener, SENSOR_OPTION_ALWAYS_ON);
314 ret = sensor_listener_start(light_listener);
315 if (ret != SENSOR_ERROR_NONE) {
316 _E("Failed to start light sensor.");
317 sensor_destroy_listener(light_listener);
322 if (!g_display_plugin.config->accel_sensor_on)
325 /* accelerometer sensor */
326 ret = sensor_get_default_sensor(SENSOR_ACCELEROMETER, &sensor);
327 if (ret != SENSOR_ERROR_NONE) {
328 _E("Failed to get default accel sensor.");
331 ret = sensor_create_listener(&sensor, &accel_listener);
332 if (ret != SENSOR_ERROR_NONE) {
333 _E("Failed to create listener(accel).");
336 sensor_listener_set_option(accel_listener, SENSOR_OPTION_ALWAYS_ON);
337 ret = sensor_listener_start(accel_listener);
338 if (ret != SENSOR_ERROR_NONE) {
339 _E("Failed to start accel sensor.");
340 sensor_destroy_listener(accel_listener);
350 if (light_listener > 0) {
351 sensor_listener_stop(light_listener);
352 sensor_destroy_listener(light_listener);
355 if (g_display_plugin.config->accel_sensor_on && accel_listener > 0) {
356 sensor_listener_stop(accel_listener);
357 sensor_destroy_listener(accel_listener);
363 static int disconnect_sensor(void)
365 assert(g_display_plugin.config);
367 _I("Disconnect with sensor fw.");
369 if (light_listener > 0) {
370 sensor_listener_stop(light_listener);
371 sensor_destroy_listener(light_listener);
375 /* accelerometer sensor*/
376 if (g_display_plugin.config->accel_sensor_on && accel_listener > 0) {
377 sensor_listener_stop(accel_listener);
378 sensor_destroy_listener(accel_listener);
382 if (_default_action != NULL) {
383 state_st(DEVICED_DISPLAY_STATE_ON)->action = _default_action;
384 _default_action = NULL;
386 if (alc_timeout_id > 0) {
387 g_source_remove(alc_timeout_id);
394 void set_brightness_changed_state(void)
396 if (get_pm_status_flag() & PWRSV_FLAG) {
397 set_pm_status_flag(BRTCH_FLAG);
398 _D("Brightness changed in low battery,"
399 "escape dim state (light).");
403 static int set_autobrightness_state(int status)
407 int default_brt = -1;
409 assert(g_display_plugin.config);
411 if (status == SETTING_BRIGHTNESS_AUTOMATIC_ON) {
412 if (connect_sensor() < 0)
415 /* escape dim state if it's in low battery.*/
416 set_brightness_changed_state();
418 /* change alc action func */
419 if (_default_action == NULL)
420 _default_action = state_st(DEVICED_DISPLAY_STATE_ON)->action;
421 state_st(DEVICED_DISPLAY_STATE_ON)->action = alc_action;
423 update_auto_brightness(true);
426 g_timeout_add_seconds(g_display_plugin.config->lightsensor_interval,
428 } else if (status == SETTING_BRIGHTNESS_AUTOMATIC_PAUSE) {
429 _I("Auto brightness paused.");
433 /* escape dim state if it's in low battery.*/
434 set_brightness_changed_state();
436 ret = get_setting_brightness(&default_brt);
437 if (ret != 0 || (default_brt < PM_MIN_BRIGHTNESS || default_brt > PM_MAX_BRIGHTNESS)) {
438 _I("Failed to read vconf value for brightness.");
439 brt = g_display_plugin.config->pm_default_brightness;
440 if (default_brt < PM_MIN_BRIGHTNESS || default_brt > PM_MAX_BRIGHTNESS) {
441 ret = vconf_set_int(VCONFKEY_SETAPPL_LCD_BRIGHTNESS, brt);
443 _E("Failed to set vconf value for lcd brightness: %d", vconf_get_ext_errno());
448 display_backlight_set_default_brightness(default_brt);
449 display_backlight_update_by_default_brightness();
455 static void set_alc_function(keynode_t *key_nodes, void *data)
459 if (key_nodes == NULL) {
460 _E("Wrong parameter, key_nodes is null.");
464 status = vconf_keynode_get_int(key_nodes);
467 case SETTING_BRIGHTNESS_AUTOMATIC_OFF:
468 case SETTING_BRIGHTNESS_AUTOMATIC_ON:
469 case SETTING_BRIGHTNESS_AUTOMATIC_PAUSE:
470 ret = set_autobrightness_state(status);
471 _D("Set auto brightness: %d", ret);
474 _E("Invalid value(%d)", status);
478 static void set_alc_automatic_brt(keynode_t *key_nodes, void *data)
480 if (key_nodes == NULL) {
481 _E("Wrong parameter, key_nodes is null.");
484 automatic_brt = vconf_keynode_get_int(key_nodes) / AUTOMATIC_DEVIDE_VAL;
485 _D("Automatic brt(%d)", automatic_brt);
487 alc_update_brt(true);
490 static gboolean update_handler(void *data)
496 if (get_pm_cur_state() != DEVICED_DISPLAY_STATE_ON)
497 return G_SOURCE_REMOVE;
499 ret = vconf_get_int(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT, &on);
500 if (ret < 0 || on != SETTING_BRIGHTNESS_AUTOMATIC_ON) {
502 _E("Failed to get vconf value for automatic brightness: %d", vconf_get_ext_errno());
503 return G_SOURCE_REMOVE;
506 _D("Auto brightness is working.");
507 alc_update_brt(true);
509 return G_SOURCE_REMOVE;
512 void update_auto_brightness(bool update)
514 if (update_timeout) {
515 g_source_remove(update_timeout);
520 update_timeout = g_timeout_add(AUTOMATIC_DELAY_TIME,
521 update_handler, NULL);
525 static int prepare_lsensor(void *data)
530 ret = vconf_get_int(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT, &status);
532 if (ret == 0 && status == SETTING_BRIGHTNESS_AUTOMATIC_ON)
533 set_autobrightness_state(status);
535 _E("Failed to get vconf value for automatic brightness: %d", vconf_get_ext_errno());
537 /* add auto_brt_setting change handler */
538 vconf_notify_key_changed(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT,
539 set_alc_function, NULL);
541 ret = vconf_get_int(VCONFKEY_SETAPPL_LCD_AUTOMATIC_BRIGHTNESS, &brt);
544 _E("Failed to get vconf value for automatic lcd brightness: %d", vconf_get_ext_errno());
546 if (brt < PM_MIN_BRIGHTNESS || brt > PM_MAX_BRIGHTNESS) {
547 _E("Failed to get automatic brightness.");
549 automatic_brt = brt / AUTOMATIC_DEVIDE_VAL;
550 _I("Automatic brt(%d) init success.", automatic_brt);
553 vconf_notify_key_changed(VCONFKEY_SETAPPL_LCD_AUTOMATIC_BRIGHTNESS,
554 set_alc_automatic_brt, NULL);
559 static void update_brightness_direct(void)
563 ret = vconf_get_int(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT, &status);
564 if (ret == 0 && status == SETTING_BRIGHTNESS_AUTOMATIC_ON)
565 alc_update_brt(true);
567 _E("Failed to get vconf value for automatic brightness: %d", vconf_get_ext_errno());
570 static int set_autobrightness_min(int val, char *name)
575 if (val < PM_MIN_BRIGHTNESS || val > PM_MAX_BRIGHTNESS)
578 min_brightness = val;
580 if (min_brightness_name) {
581 free(min_brightness_name);
582 min_brightness_name = 0;
584 min_brightness_name = strndup(name, strlen(name));
586 update_brightness_direct();
588 _I("Auto brightness min value changed. min_brightness=%d min_brightness_name=%s",
589 min_brightness, min_brightness_name);
594 static void reset_autobrightness_min(GDBusConnection *conn,
596 const gchar *unique_name,
602 if (!min_brightness_name)
605 if (strcmp(sender, min_brightness_name))
608 _I("Change to default min brightness. before=%d changed=%d brightness_name=%s", min_brightness,
609 PM_MIN_BRIGHTNESS, min_brightness_name);
610 min_brightness = PM_MIN_BRIGHTNESS;
611 if (min_brightness_name) {
612 free(min_brightness_name);
613 min_brightness_name = 0;
616 update_brightness_direct();
619 static int lcd_changed_cb(void *data)
625 lcd_state = *(int *)data;
626 if (lcd_state == DEVICED_DISPLAY_STATE_OFF && alc_timeout_id > 0) {
627 g_source_remove(alc_timeout_id);
634 static int delayed_init_done(void *data)
641 state = *(int *)data;
645 /* get light data from sensor fw */
646 prepare_lsensor(NULL);
651 static void exit_lsensor(void)
653 vconf_ignore_key_changed(VCONFKEY_SETAPPL_BRIGHTNESS_AUTOMATIC_INT,
656 vconf_ignore_key_changed(VCONFKEY_SETAPPL_LCD_AUTOMATIC_BRIGHTNESS,
657 set_alc_automatic_brt);
659 set_autobrightness_state(SETTING_BRIGHTNESS_AUTOMATIC_OFF);
662 static void auto_brightness_init(void *data)
664 g_display_plugin.set_autobrightness_min = set_autobrightness_min;
665 g_display_plugin.reset_autobrightness_min = reset_autobrightness_min;
667 syscommon_notifier_subscribe_notify(DEVICED_NOTIFIER_LCD, lcd_changed_cb);
668 syscommon_notifier_subscribe_notify(DEVICED_NOTIFIER_DELAYED_INIT, delayed_init_done);
671 static void auto_brightness_exit(void *data)
675 syscommon_notifier_unsubscribe_notify(DEVICED_NOTIFIER_LCD, lcd_changed_cb);
676 syscommon_notifier_unsubscribe_notify(DEVICED_NOTIFIER_DELAYED_INIT, delayed_init_done);
679 static const struct display_ops display_autobrightness_ops = {
680 .name = "auto-brightness",
681 .init = auto_brightness_init,
682 .exit = auto_brightness_exit,
685 DISPLAY_OPS_REGISTER(&display_autobrightness_ops)