| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * Copyright (C) 2019 Amlogic, Inc. All rights reserved. |
| */ |
| |
| #include <linux/init.h> |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/platform_device.h> |
| #include <linux/fb.h> |
| #include <linux/clk.h> |
| #include <linux/gpio.h> |
| #include <linux/backlight.h> |
| #include <linux/slab.h> |
| #include <linux/workqueue.h> |
| #include <linux/delay.h> |
| #include <linux/notifier.h> |
| #include <linux/of_device.h> |
| #include <linux/interrupt.h> |
| #include <linux/pwm.h> |
| #include <linux/amlogic/pwm-meson.h> |
| #include <linux/amlogic/cpu_version.h> |
| #include <linux/amlogic/aml_gpio_consumer.h> |
| #include <linux/pinctrl/consumer.h> |
| #include <linux/amlogic/media/vout/lcd/aml_bl.h> |
| #include <linux/amlogic/media/vout/lcd/lcd_notify.h> |
| #include <linux/amlogic/media/vout/lcd/lcd_unifykey.h> |
| #include <linux/compat.h> |
| #include <linux/amlogic/media/vout/lcd/lcd_vout.h> |
| #ifdef CONFIG_AMLOGIC_BL_EXTERN |
| #include <linux/amlogic/media/vout/lcd/aml_bl_extern.h> |
| #endif |
| #ifdef CONFIG_AMLOGIC_BL_LDIM |
| #include <linux/amlogic/media/vout/lcd/aml_ldim.h> |
| #endif |
| #include "lcd_bl.h" |
| #include "../lcd_reg.h" |
| #include "../lcd_common.h" |
| |
| #include <linux/amlogic/gki_module.h> |
| |
| #define BL_CDEV_NAME "aml_bl" |
| struct bl_cdev_s { |
| dev_t devno; |
| struct class *class; |
| }; |
| |
| static struct bl_cdev_s *bl_cdev; |
| /* for driver global resource init: |
| * 0: none |
| * n: initialized cnt |
| */ |
| static unsigned char bl_global_init_flag; |
| static unsigned int bl_drv_init_state; |
| static struct aml_bl_drv_s *bl_drv[LCD_MAX_DRV]; |
| static int bl_index_lut[LCD_MAX_DRV] = {0xff, 0xff, 0xff}; |
| static unsigned int bl_level[LCD_MAX_DRV]; |
| |
| static DEFINE_MUTEX(bl_status_mutex); |
| static DEFINE_MUTEX(bl_power_mutex); |
| static DEFINE_MUTEX(bl_level_mutex); |
| |
| struct bl_method_match_s { |
| char *name; |
| enum bl_ctrl_method_e type; |
| }; |
| |
| static struct bl_method_match_s bl_method_match_table[] = { |
| {"gpio", BL_CTRL_GPIO}, |
| {"pwm", BL_CTRL_PWM}, |
| {"pwm_combo", BL_CTRL_PWM_COMBO}, |
| {"local_dimming", BL_CTRL_LOCAL_DIMMING}, |
| {"extern", BL_CTRL_EXTERN}, |
| {"invalid", BL_CTRL_MAX}, |
| }; |
| |
| static char *bl_method_type_to_str(int type) |
| { |
| int i; |
| char *str = bl_method_match_table[BL_CTRL_MAX].name; |
| |
| for (i = 0; i < BL_CTRL_MAX; i++) { |
| if (type == bl_method_match_table[i].type) { |
| str = bl_method_match_table[i].name; |
| break; |
| } |
| } |
| return str; |
| } |
| |
| static int aml_bl_check_driver(struct aml_bl_drv_s *bdrv) |
| { |
| int ret = 0; |
| |
| if (!bdrv) { |
| /*BLERR("no bl driver\n");*/ |
| return -1; |
| } |
| switch (bdrv->bconf.method) { |
| case BL_CTRL_PWM: |
| if (!bdrv->bconf.bl_pwm) { |
| ret = -1; |
| BLERR("no bl_pwm struct\n"); |
| } |
| break; |
| case BL_CTRL_PWM_COMBO: |
| if (!bdrv->bconf.bl_pwm_combo0) { |
| ret = -1; |
| BLERR("no bl_pwm_combo_0 struct\n"); |
| } |
| if (!bdrv->bconf.bl_pwm_combo1) { |
| ret = -1; |
| BLERR("no bl_pwm_combo_1 struct\n"); |
| } |
| break; |
| case BL_CTRL_MAX: |
| ret = -1; |
| break; |
| default: |
| break; |
| } |
| |
| return ret; |
| } |
| |
| struct aml_bl_drv_s *aml_bl_get_driver(int index) |
| { |
| if (index >= LCD_MAX_DRV) { |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_ADV) |
| BLERR("%s: invalid index: %d\n", __func__, index); |
| return NULL; |
| } |
| if (!bl_drv[index]) { |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_ADV) |
| BLERR("no bl driver"); |
| return NULL; |
| } |
| |
| return bl_drv[index]; |
| } |
| |
| static void bl_gpio_probe(struct aml_bl_drv_s *bdrv, int index) |
| { |
| struct bl_gpio_s *bl_gpio; |
| const char *str; |
| int ret; |
| |
| if (index >= BL_GPIO_NUM_MAX) { |
| BLERR("gpio index %d, exit\n", index); |
| return; |
| } |
| bl_gpio = &bdrv->bconf.bl_gpio[index]; |
| if (bl_gpio->probe_flag) { |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) { |
| BLPR("gpio %s[%d] is already registered\n", |
| bl_gpio->name, index); |
| } |
| return; |
| } |
| |
| /* get gpio name */ |
| ret = of_property_read_string_index(bdrv->dev->of_node, |
| "bl_gpio_names", index, &str); |
| if (ret) { |
| BLERR("failed to get bl_gpio_names: %d\n", index); |
| str = "unknown"; |
| } |
| strcpy(bl_gpio->name, str); |
| |
| /* init gpio flag */ |
| bl_gpio->probe_flag = 1; |
| bl_gpio->register_flag = 0; |
| } |
| |
| static int bl_gpio_register(struct aml_bl_drv_s *bdrv, int index, int init_value) |
| { |
| struct bl_gpio_s *bl_gpio; |
| int value; |
| |
| if (index >= BL_GPIO_NUM_MAX) { |
| BLERR("%s: gpio index %d, exit\n", __func__, index); |
| return -1; |
| } |
| bl_gpio = &bdrv->bconf.bl_gpio[index]; |
| if (bl_gpio->probe_flag == 0) { |
| BLERR("%s: gpio [%d] is not probed, exit\n", __func__, index); |
| return -1; |
| } |
| if (bl_gpio->register_flag) { |
| BLPR("%s: gpio %s[%d] is already registered\n", |
| __func__, bl_gpio->name, index); |
| return 0; |
| } |
| |
| switch (init_value) { |
| case BL_GPIO_OUTPUT_LOW: |
| value = GPIOD_OUT_LOW; |
| break; |
| case BL_GPIO_OUTPUT_HIGH: |
| value = GPIOD_OUT_HIGH; |
| break; |
| case BL_GPIO_INPUT: |
| default: |
| value = GPIOD_IN; |
| break; |
| } |
| |
| /* request gpio */ |
| bl_gpio->gpio = devm_gpiod_get_index(bdrv->dev, "bl", index, value); |
| if (IS_ERR(bl_gpio->gpio)) { |
| BLERR("register gpio %s[%d]: %p, err: %d\n", |
| bl_gpio->name, index, bl_gpio->gpio, |
| IS_ERR(bl_gpio->gpio)); |
| return -1; |
| } |
| |
| bl_gpio->register_flag = 1; |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) { |
| BLPR("register gpio %s[%d]: %p, init value: %d\n", |
| bl_gpio->name, index, bl_gpio->gpio, init_value); |
| } |
| |
| return 0; |
| } |
| |
| static void bl_gpio_set(struct aml_bl_drv_s *bdrv, int index, int value) |
| { |
| struct bl_gpio_s *bl_gpio; |
| |
| if (index >= BL_GPIO_NUM_MAX) { |
| BLERR("gpio index %d, exit\n", index); |
| return; |
| } |
| bl_gpio = &bdrv->bconf.bl_gpio[index]; |
| if (bl_gpio->probe_flag == 0) { |
| BLERR("%s: gpio [%d] is not probed, exit\n", __func__, index); |
| return; |
| } |
| if (bl_gpio->register_flag == 0) { |
| bl_gpio_register(bdrv, index, value); |
| return; |
| } |
| |
| if (IS_ERR_OR_NULL(bl_gpio->gpio)) { |
| BLERR("gpio %s[%d]: %p, err: %ld\n", |
| bl_gpio->name, index, bl_gpio->gpio, |
| PTR_ERR(bl_gpio->gpio)); |
| return; |
| } |
| |
| switch (value) { |
| case BL_GPIO_OUTPUT_LOW: |
| case BL_GPIO_OUTPUT_HIGH: |
| gpiod_direction_output(bl_gpio->gpio, value); |
| break; |
| case BL_GPIO_INPUT: |
| default: |
| gpiod_direction_input(bl_gpio->gpio); |
| break; |
| } |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) { |
| BLPR("set gpio %s[%d] value: %d\n", |
| bl_gpio->name, index, value); |
| } |
| } |
| |
| /* ****************************************************** */ |
| #define BL_PINMUX_MAX 8 |
| static char *bl_pinmux_str[BL_PINMUX_MAX] = { |
| "pwm_on", /* 0 */ |
| "pwm_vs_on", /* 1 */ |
| "pwm_combo_0_1_on", /* 2 */ |
| "pwm_combo_0_vs_1_on", /* 3 */ |
| "pwm_combo_0_1_vs_on", /* 4 */ |
| "pwm_off", /* 5 */ |
| "pwm_combo_off", /* 6 */ |
| "none", |
| }; |
| |
| static void bl_pwm_pinmux_set(struct aml_bl_drv_s *bdrv, int status) |
| { |
| struct bl_config_s *bconf = &bdrv->bconf; |
| int index = 0xff; |
| |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) |
| BLPR("[%d]: %s\n", bdrv->index, __func__); |
| |
| switch (bconf->method) { |
| case BL_CTRL_PWM: |
| if (status) { |
| if (bconf->bl_pwm->pwm_port == BL_PWM_VS) |
| index = 1; |
| else |
| index = 0; |
| } else { |
| index = 5; |
| } |
| break; |
| case BL_CTRL_PWM_COMBO: |
| if (status) { |
| if (bconf->bl_pwm_combo0->pwm_port == BL_PWM_VS) { |
| index = 3; |
| } else { |
| if (bconf->bl_pwm_combo1->pwm_port == BL_PWM_VS) |
| index = 4; |
| else |
| index = 2; |
| } |
| } else { |
| index = 6; |
| } |
| break; |
| default: |
| BLERR("[%d]: %s: wrong ctrl_mothod=%d\n", |
| bdrv->index, __func__, bconf->method); |
| break; |
| } |
| |
| if (index >= BL_PINMUX_MAX) { |
| BLERR("[%d]: %s: pinmux index %d is invalid\n", |
| bdrv->index, __func__, index); |
| return; |
| } |
| |
| if (bdrv->pinmux_flag == index) { |
| BLPR("[%d]: pinmux %s is already selected\n", |
| bdrv->index, bl_pinmux_str[index]); |
| return; |
| } |
| |
| /* request pwm pinmux */ |
| bdrv->pin = devm_pinctrl_get_select(bdrv->dev, bl_pinmux_str[index]); |
| if (IS_ERR(bdrv->pin)) { |
| BLERR("[%d]: set pinmux %s error\n", |
| bdrv->index, bl_pinmux_str[index]); |
| } else { |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) { |
| BLPR("[%d]: set pinmux %s: %p\n", |
| bdrv->index, bl_pinmux_str[index], bdrv->pin); |
| } |
| } |
| bdrv->pinmux_flag = index; |
| } |
| |
| /* ****************************************************** */ |
| static void bl_power_en_ctrl(struct aml_bl_drv_s *bdrv, int status) |
| { |
| struct bl_config_s *bconf = &bdrv->bconf; |
| |
| if (status) { |
| if (bconf->en_gpio < BL_GPIO_NUM_MAX) |
| bl_gpio_set(bdrv, bconf->en_gpio, bconf->en_gpio_on); |
| } else { |
| if (bconf->en_gpio < BL_GPIO_NUM_MAX) |
| bl_gpio_set(bdrv, bconf->en_gpio, bconf->en_gpio_off); |
| } |
| } |
| |
| static void bl_power_on(struct aml_bl_drv_s *bdrv) |
| { |
| struct bl_config_s *bconf = &bdrv->bconf; |
| #ifdef CONFIG_AMLOGIC_BL_EXTERN |
| struct bl_extern_driver_s *bext; |
| #endif |
| #ifdef CONFIG_AMLOGIC_BL_LDIM |
| struct aml_ldim_driver_s *ldim_drv; |
| #endif |
| int ret; |
| |
| if (aml_bl_check_driver(bdrv)) |
| return; |
| |
| mutex_lock(&bl_power_mutex); |
| |
| if ((bdrv->state & BL_STATE_LCD_ON) == 0) { |
| BLPR("%s exit, for lcd is off\n", __func__); |
| goto exit_power_on_bl; |
| } |
| if ((bdrv->state & BL_STATE_BL_POWER_ON) == 0) { |
| BLPR("%s exit, for backlight power off\n", __func__); |
| goto exit_power_on_bl; |
| } |
| |
| if (bdrv->brightness_bypass == 0) { |
| if (bdrv->level == 0 || (bdrv->state & BL_STATE_BL_ON)) |
| goto exit_power_on_bl; |
| } |
| |
| ret = 0; |
| switch (bconf->method) { |
| case BL_CTRL_GPIO: |
| bl_power_en_ctrl(bdrv, 1); |
| break; |
| case BL_CTRL_PWM: |
| if (bconf->en_sequence_reverse) { |
| /* step 1: power on enable */ |
| bl_power_en_ctrl(bdrv, 1); |
| if (bconf->pwm_on_delay > 0) |
| msleep(bconf->pwm_on_delay); |
| /* step 2: power on pwm */ |
| bl_pwm_ctrl(bconf->bl_pwm, 1); |
| bl_pwm_pinmux_set(bdrv, 1); |
| } else { |
| /* step 1: power on pwm */ |
| bl_pwm_ctrl(bconf->bl_pwm, 1); |
| bl_pwm_pinmux_set(bdrv, 1); |
| if (bconf->pwm_on_delay > 0) |
| msleep(bconf->pwm_on_delay); |
| /* step 2: power on enable */ |
| bl_power_en_ctrl(bdrv, 1); |
| } |
| break; |
| case BL_CTRL_PWM_COMBO: |
| if (bconf->en_sequence_reverse) { |
| /* step 1: power on enable */ |
| bl_power_en_ctrl(bdrv, 1); |
| if (bconf->pwm_on_delay > 0) |
| msleep(bconf->pwm_on_delay); |
| /* step 2: power on pwm_combo */ |
| bl_pwm_ctrl(bconf->bl_pwm_combo0, 1); |
| bl_pwm_ctrl(bconf->bl_pwm_combo1, 1); |
| bl_pwm_pinmux_set(bdrv, 1); |
| } else { |
| /* step 1: power on pwm_combo */ |
| bl_pwm_ctrl(bconf->bl_pwm_combo0, 1); |
| bl_pwm_ctrl(bconf->bl_pwm_combo1, 1); |
| bl_pwm_pinmux_set(bdrv, 1); |
| if (bconf->pwm_on_delay > 0) |
| msleep(bconf->pwm_on_delay); |
| /* step 2: power on enable */ |
| bl_power_en_ctrl(bdrv, 1); |
| } |
| break; |
| #ifdef CONFIG_AMLOGIC_BL_LDIM |
| case BL_CTRL_LOCAL_DIMMING: |
| ldim_drv = aml_ldim_get_driver(); |
| if (!ldim_drv) { |
| BLERR("no ldim driver\n"); |
| goto exit_power_on_bl; |
| } |
| if (bconf->en_sequence_reverse) { |
| /* step 1: power on enable */ |
| bl_power_en_ctrl(bdrv, 1); |
| /* step 2: power on ldim */ |
| if (ldim_drv->power_on) { |
| ret = ldim_drv->power_on(); |
| if (ret) |
| BLERR("ldim: power on error\n"); |
| } else { |
| BLPR("ldim: power on is null\n"); |
| } |
| } else { |
| /* step 1: power on ldim */ |
| if (ldim_drv->power_on) { |
| ret = ldim_drv->power_on(); |
| if (ret) |
| BLERR("ldim: power on error\n"); |
| } else { |
| BLPR("ldim: power on is null\n"); |
| } |
| /* step 2: power on enable */ |
| bl_power_en_ctrl(bdrv, 1); |
| } |
| break; |
| #endif |
| #ifdef CONFIG_AMLOGIC_BL_EXTERN |
| case BL_CTRL_EXTERN: |
| bext = bl_extern_get_driver(bdrv->index); |
| if (!bext) { |
| BLERR("[%d]: no bl_extern driver\n", bdrv->index); |
| goto exit_power_on_bl; |
| } |
| if (bconf->en_sequence_reverse) { |
| /* step 1: power on enable */ |
| bl_power_en_ctrl(bdrv, 1); |
| /* step 2: power on bl_extern */ |
| if (bext->power_on) { |
| ret = bext->power_on(bext); |
| if (ret) |
| BLERR("bl_extern: power on error\n"); |
| } |
| } else { |
| /* step 1: power on bl_extern */ |
| if (bext->power_on) { |
| ret = bext->power_on(bext); |
| if (ret) |
| BLERR("bl_extern: power on error\n"); |
| } |
| /* step 2: power on enable */ |
| bl_power_en_ctrl(bdrv, 1); |
| } |
| break; |
| #endif |
| default: |
| BLPR("invalid backlight control method\n"); |
| goto exit_power_on_bl; |
| } |
| bdrv->state |= BL_STATE_BL_ON; |
| BLPR("backlight power on\n"); |
| |
| exit_power_on_bl: |
| mutex_unlock(&bl_power_mutex); |
| } |
| |
| static void bl_power_off(struct aml_bl_drv_s *bdrv) |
| { |
| struct bl_config_s *bconf = &bdrv->bconf; |
| #ifdef CONFIG_AMLOGIC_BL_EXTERN |
| struct bl_extern_driver_s *bext; |
| #endif |
| #ifdef CONFIG_AMLOGIC_BL_LDIM |
| struct aml_ldim_driver_s *ldim_drv; |
| #endif |
| int ret; |
| |
| if (aml_bl_check_driver(bdrv)) |
| return; |
| mutex_lock(&bl_power_mutex); |
| |
| if ((bdrv->state & BL_STATE_BL_ON) == 0) { |
| goto exit_power_off_bl; |
| return; |
| } |
| |
| ret = 0; |
| switch (bconf->method) { |
| case BL_CTRL_GPIO: |
| bl_power_en_ctrl(bdrv, 0); |
| break; |
| case BL_CTRL_PWM: |
| if (bconf->en_sequence_reverse == 1) { |
| /* step 1: power off pwm */ |
| bl_pwm_pinmux_set(bdrv, 0); |
| bl_pwm_ctrl(bconf->bl_pwm, 0); |
| if (bconf->pwm_off_delay > 0) |
| msleep(bconf->pwm_off_delay); |
| /* step 2: power off enable */ |
| bl_power_en_ctrl(bdrv, 0); |
| } else { |
| /* step 1: power off enable */ |
| bl_power_en_ctrl(bdrv, 0); |
| /* step 2: power off pwm */ |
| if (bconf->pwm_off_delay > 0) |
| msleep(bconf->pwm_off_delay); |
| bl_pwm_pinmux_set(bdrv, 0); |
| bl_pwm_ctrl(bconf->bl_pwm, 0); |
| } |
| break; |
| case BL_CTRL_PWM_COMBO: |
| if (bconf->en_sequence_reverse == 1) { |
| /* step 1: power off pwm_combo */ |
| bl_pwm_pinmux_set(bdrv, 0); |
| bl_pwm_ctrl(bconf->bl_pwm_combo0, 0); |
| bl_pwm_ctrl(bconf->bl_pwm_combo1, 0); |
| if (bconf->pwm_off_delay > 0) |
| msleep(bconf->pwm_off_delay); |
| /* step 2: power off enable */ |
| bl_power_en_ctrl(bdrv, 0); |
| } else { |
| /* step 1: power off enable */ |
| bl_power_en_ctrl(bdrv, 0); |
| /* step 2: power off pwm_combo */ |
| if (bconf->pwm_off_delay > 0) |
| msleep(bconf->pwm_off_delay); |
| bl_pwm_pinmux_set(bdrv, 0); |
| bl_pwm_ctrl(bconf->bl_pwm_combo0, 0); |
| bl_pwm_ctrl(bconf->bl_pwm_combo1, 0); |
| } |
| break; |
| #ifdef CONFIG_AMLOGIC_BL_LDIM |
| case BL_CTRL_LOCAL_DIMMING: |
| ldim_drv = aml_ldim_get_driver(); |
| if (!ldim_drv) { |
| BLERR("no ldim driver\n"); |
| goto exit_power_off_bl; |
| } |
| if (bconf->en_sequence_reverse == 1) { |
| /* step 1: power off ldim */ |
| if (ldim_drv->power_off) { |
| ret = ldim_drv->power_off(); |
| if (ret) |
| BLERR("ldim: power off error\n"); |
| } else { |
| BLERR("ldim: power off is null\n"); |
| } |
| /* step 2: power off enable */ |
| bl_power_en_ctrl(bdrv, 0); |
| } else { |
| /* step 1: power off enable */ |
| bl_power_en_ctrl(bdrv, 0); |
| /* step 2: power off ldim */ |
| if (ldim_drv->power_off) { |
| ret = ldim_drv->power_off(); |
| if (ret) |
| BLERR("ldim: power off error\n"); |
| } else { |
| BLERR("ldim: power off is null\n"); |
| } |
| } |
| break; |
| #endif |
| #ifdef CONFIG_AMLOGIC_BL_EXTERN |
| case BL_CTRL_EXTERN: |
| bext = bl_extern_get_driver(bdrv->index); |
| if (!bext) { |
| BLERR("[%d]: no bl_extern driver\n", bdrv->index); |
| goto exit_power_off_bl; |
| } |
| if (bconf->en_sequence_reverse == 1) { |
| /* step 1: power off bl_extern */ |
| if (bext->power_off) { |
| ret = bext->power_off(bext); |
| if (ret) |
| BLERR("bl_extern: power off error\n"); |
| } |
| /* step 2: power off enable */ |
| bl_power_en_ctrl(bdrv, 0); |
| } else { |
| /* step 1: power off enable */ |
| bl_power_en_ctrl(bdrv, 0); |
| /* step 2: power off bl_extern */ |
| if (bext->power_off) { |
| ret = bext->power_off(bext); |
| if (ret) |
| BLERR("bl_extern: power off error\n"); |
| } |
| } |
| break; |
| #endif |
| default: |
| BLPR("invalid backlight control method\n"); |
| goto exit_power_off_bl; |
| break; |
| } |
| if (bconf->power_off_delay > 0) |
| lcd_delay_ms(bconf->power_off_delay); |
| |
| bdrv->state &= ~BL_STATE_BL_ON; |
| BLPR("backlight power off\n"); |
| |
| exit_power_off_bl: |
| mutex_unlock(&bl_power_mutex); |
| } |
| |
| #ifdef CONFIG_AMLOGIC_BL_EXTERN |
| static void bl_set_level_extern(struct aml_bl_drv_s *bdrv, unsigned int level) |
| { |
| struct bl_extern_driver_s *bext; |
| int ret; |
| |
| bext = bl_extern_get_driver(bdrv->index); |
| if (!bext) { |
| BLERR("[%d]: no bl_extern driver\n", bdrv->index); |
| return; |
| } |
| |
| if (bext->set_level) { |
| ret = bext->set_level(bext, level); |
| if (ret) |
| BLERR("bl_ext: set_level error\n"); |
| } else { |
| BLERR("bl_ext: set_level is null\n"); |
| } |
| } |
| #endif |
| |
| #ifdef CONFIG_AMLOGIC_BL_LDIM |
| static void bl_set_level_ldim(unsigned int level) |
| { |
| struct aml_ldim_driver_s *ldim_drv; |
| int ret = 0; |
| |
| ldim_drv = aml_ldim_get_driver(); |
| if (!ldim_drv) { |
| BLERR("no ldim driver\n"); |
| } else { |
| if (ldim_drv->set_level) { |
| ret = ldim_drv->set_level(level); |
| if (ret) |
| BLERR("ldim: set_level error\n"); |
| } else { |
| BLERR("ldim: set_level is null\n"); |
| } |
| } |
| } |
| #endif |
| |
| static void aml_bl_set_level(struct aml_bl_drv_s *bdrv, unsigned int level) |
| { |
| struct bl_pwm_config_s *pwm0, *pwm1; |
| |
| if (aml_bl_check_driver(bdrv)) |
| return; |
| |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_ADV) { |
| BLPR("bl_set_level=%u, last_level=%u, state=0x%x\n", |
| level, bdrv->level, bdrv->state); |
| } |
| |
| /* level range check */ |
| if (level > bdrv->bconf.level_max) |
| level = bdrv->bconf.level_max; |
| if (level < bdrv->bconf.level_min) { |
| if (level < BL_LEVEL_OFF) |
| level = 0; |
| else |
| level = bdrv->bconf.level_min; |
| } |
| bdrv->level = level; |
| |
| if (level == 0) |
| return; |
| |
| switch (bdrv->bconf.method) { |
| case BL_CTRL_GPIO: |
| break; |
| case BL_CTRL_PWM: |
| bl_pwm_set_level(bdrv, bdrv->bconf.bl_pwm, level); |
| break; |
| case BL_CTRL_PWM_COMBO: |
| pwm0 = bdrv->bconf.bl_pwm_combo0; |
| pwm1 = bdrv->bconf.bl_pwm_combo1; |
| |
| if (level >= pwm0->level_max) { |
| bl_pwm_set_level(bdrv, pwm0, pwm0->level_max); |
| } else if ((level > pwm0->level_min) && |
| (level < pwm0->level_max)) { |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_ADV) |
| BLPR("pwm0 region, level=%u\n", level); |
| bl_pwm_set_level(bdrv, pwm0, level); |
| } else { |
| bl_pwm_set_level(bdrv, pwm0, pwm0->level_min); |
| } |
| |
| if (level >= pwm1->level_max) { |
| bl_pwm_set_level(bdrv, pwm1, pwm1->level_max); |
| } else if ((level > pwm1->level_min) && |
| (level < pwm1->level_max)) { |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_ADV) |
| BLPR("pwm1 region, level=%u,\n", level); |
| bl_pwm_set_level(bdrv, pwm1, level); |
| } else { |
| bl_pwm_set_level(bdrv, pwm1, pwm1->level_min); |
| } |
| break; |
| #ifdef CONFIG_AMLOGIC_BL_LDIM |
| case BL_CTRL_LOCAL_DIMMING: |
| bl_set_level_ldim(level); |
| break; |
| #endif |
| #ifdef CONFIG_AMLOGIC_BL_EXTERN |
| case BL_CTRL_EXTERN: |
| bl_set_level_extern(bdrv, level); |
| break; |
| #endif |
| default: |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) |
| BLPR("invalid backlight control method\n"); |
| break; |
| } |
| } |
| |
| static unsigned int aml_bl_get_level_brightness(struct aml_bl_drv_s *bdrv) |
| { |
| if (aml_bl_check_driver(bdrv)) |
| return 0; |
| |
| BLPR("aml bl state: 0x%x\n", bdrv->state); |
| return bdrv->level_brightness; |
| } |
| |
| static inline unsigned int bl_brightness_level_map(struct aml_bl_drv_s *bdrv, |
| unsigned int brightness) |
| { |
| unsigned int level; |
| |
| if (brightness == 0) |
| level = 0; |
| else if (brightness > bdrv->bconf.level_max) |
| level = bdrv->bconf.level_max; |
| else if (brightness < bdrv->bconf.level_min) |
| level = bdrv->bconf.level_min; |
| else |
| level = brightness; |
| |
| return level; |
| } |
| |
| static inline unsigned int bl_gd_level_map(struct aml_bl_drv_s *bdrv, |
| unsigned int gd_level) |
| { |
| unsigned int max, min, val; |
| |
| min = bdrv->bconf.level_min; |
| max = bdrv->bconf.level_max; |
| val = (gd_level * (bdrv->bldev->props.brightness - min)) / 4095 + min; |
| |
| return val; |
| } |
| |
| static unsigned int aml_bl_init_level(struct aml_bl_drv_s *bdrv, |
| unsigned int level) |
| { |
| unsigned int bl_level = level; |
| |
| if (((bdrv->state & BL_STATE_LCD_ON) == 0) || |
| ((bdrv->state & BL_STATE_BL_POWER_ON) == 0)) |
| bl_level = 0; |
| |
| if (bl_level == 0) { |
| if (bdrv->state & BL_STATE_BL_ON) |
| bl_power_off(bdrv); |
| } else { |
| aml_bl_set_level(bdrv, bl_level); |
| if ((bdrv->state & BL_STATE_BL_ON) == 0) |
| bl_power_on(bdrv); |
| } |
| |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) { |
| BLPR("[%d]: %s: %u, final level: %u, state: 0x%x\n", |
| bdrv->index, __func__, level, bl_level, bdrv->state); |
| } |
| |
| return 0; |
| } |
| |
| static int aml_bl_update_status(struct backlight_device *bd) |
| { |
| struct aml_bl_drv_s *bdrv = (struct aml_bl_drv_s *)bl_get_data(bd); |
| unsigned int level; |
| |
| if (bdrv->brightness_bypass) |
| return 0; |
| |
| mutex_lock(&bl_status_mutex); |
| bdrv->level_brightness = bl_brightness_level_map(bdrv, |
| bdrv->bldev->props.brightness); |
| |
| if (bdrv->level_brightness == 0) { |
| if (bdrv->state & BL_STATE_BL_ON) |
| bl_power_off(bdrv); |
| } else { |
| if ((bdrv->state & BL_STATE_GD_EN) == 0) { |
| aml_bl_set_level(bdrv, bdrv->level_brightness); |
| } else { |
| level = bl_gd_level_map(bdrv, bdrv->level_gd); |
| aml_bl_set_level(bdrv, level); |
| } |
| |
| if ((bdrv->state & BL_STATE_BL_ON) == 0) |
| bl_power_on(bdrv); |
| } |
| mutex_unlock(&bl_status_mutex); |
| |
| return 0; |
| } |
| |
| static int aml_bl_get_brightness(struct backlight_device *bd) |
| { |
| struct aml_bl_drv_s *bdrv = (struct aml_bl_drv_s *)bl_get_data(bd); |
| |
| return aml_bl_get_level_brightness(bdrv); |
| } |
| |
| static const struct backlight_ops aml_bl_ops = { |
| .get_brightness = aml_bl_get_brightness, |
| .update_status = aml_bl_update_status, |
| }; |
| |
| static void bl_config_print(struct aml_bl_drv_s *bdrv) |
| { |
| struct bl_config_s *bconf = &bdrv->bconf; |
| struct bl_pwm_config_s *bl_pwm; |
| |
| if (bconf->method == BL_CTRL_MAX) { |
| BLPR("[%d]: no backlight exist\n", bdrv->index); |
| return; |
| } |
| |
| BLPR("[%d]: name = %s, method = %s(%d)\n", |
| bdrv->index, bconf->name, |
| bl_method_type_to_str(bconf->method), bconf->method); |
| |
| if ((lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) == 0) |
| return; |
| |
| BLPR("level_default = %d\n", bconf->level_default); |
| BLPR("level_min = %d\n", bconf->level_min); |
| BLPR("level_max = %d\n", bconf->level_max); |
| BLPR("level_mid = %d\n", bconf->level_mid); |
| BLPR("level_mid_mapping = %d\n", bconf->level_mid_mapping); |
| |
| BLPR("en_gpio = %d\n", bconf->en_gpio); |
| BLPR("en_gpio_on = %d\n", bconf->en_gpio_on); |
| BLPR("en_gpio_off = %d\n", bconf->en_gpio_off); |
| BLPR("power_on_delay = %dms\n", bconf->power_on_delay); |
| BLPR("power_off_delay = %dms\n\n", bconf->power_off_delay); |
| |
| switch (bconf->method) { |
| case BL_CTRL_PWM: |
| BLPR("pwm_on_delay = %dms\n", bconf->pwm_on_delay); |
| BLPR("pwm_off_delay = %dms\n", bconf->pwm_off_delay); |
| BLPR("en_sequence_reverse = %d\n", bconf->en_sequence_reverse); |
| if (bconf->bl_pwm) { |
| bl_pwm = bconf->bl_pwm; |
| BLPR("pwm_index = %d\n", bl_pwm->index); |
| BLPR("pwm_port = %s(0x%x)\n", |
| bl_pwm_num_to_str(bl_pwm->pwm_port), |
| bl_pwm->pwm_port); |
| BLPR("pwm_method = %d\n", bl_pwm->pwm_method); |
| if (bl_pwm->pwm_port == BL_PWM_VS) { |
| BLPR("pwm_freq = %d x vfreq\n", |
| bl_pwm->pwm_freq); |
| } else { |
| BLPR("pwm_freq = %uHz\n", |
| bl_pwm->pwm_freq); |
| } |
| BLPR("pwm_level_max = %u\n", bl_pwm->level_max); |
| BLPR("pwm_level_min = %u\n", bl_pwm->level_min); |
| BLPR("pwm_duty_max = %d\n", bl_pwm->pwm_duty_max); |
| BLPR("pwm_duty_min = %d\n", bl_pwm->pwm_duty_min); |
| BLPR("pwm_cnt = %u\n", bl_pwm->pwm_cnt); |
| BLPR("pwm_max = %u\n", bl_pwm->pwm_max); |
| BLPR("pwm_min = %u\n", bl_pwm->pwm_min); |
| } |
| break; |
| case BL_CTRL_PWM_COMBO: |
| BLPR("pwm_on_delay = %dms\n", bconf->pwm_on_delay); |
| BLPR("pwm_off_delay = %dms\n", bconf->pwm_off_delay); |
| BLPR("en_sequence_reverse = %d\n", bconf->en_sequence_reverse); |
| /* pwm_combo_0 */ |
| if (bconf->bl_pwm_combo0) { |
| bl_pwm = bconf->bl_pwm_combo0; |
| BLPR("pwm_combo0_index = %d\n", bl_pwm->index); |
| BLPR("pwm_combo0_port = %s(0x%x)\n", |
| bl_pwm_num_to_str(bl_pwm->pwm_port), |
| bl_pwm->pwm_port); |
| BLPR("pwm_combo0_method = %d\n", bl_pwm->pwm_method); |
| if (bl_pwm->pwm_port == BL_PWM_VS) { |
| BLPR("pwm_combo0_freq = %d x vfreq\n", |
| bl_pwm->pwm_freq); |
| } else { |
| BLPR("pwm_combo0_freq = %uHz\n", |
| bl_pwm->pwm_freq); |
| } |
| BLPR("pwm_combo0_level_max = %u\n", bl_pwm->level_max); |
| BLPR("pwm_combo0_level_min = %u\n", bl_pwm->level_min); |
| BLPR("pwm_combo0_duty_max = %d\n", |
| bl_pwm->pwm_duty_max); |
| BLPR("pwm_combo0_duty_min = %d\n", |
| bl_pwm->pwm_duty_min); |
| BLPR("pwm_combo0_pwm_cnt = %u\n", bl_pwm->pwm_cnt); |
| BLPR("pwm_combo0_pwm_max = %u\n", bl_pwm->pwm_max); |
| BLPR("pwm_combo0_pwm_min = %u\n", bl_pwm->pwm_min); |
| } |
| /* pwm_combo_1 */ |
| if (bconf->bl_pwm_combo1) { |
| bl_pwm = bconf->bl_pwm_combo1; |
| BLPR("pwm_combo1_index = %d\n", bl_pwm->index); |
| BLPR("pwm_combo1_port = %s(0x%x)\n", |
| bl_pwm_num_to_str(bl_pwm->pwm_port), |
| bl_pwm->pwm_port); |
| BLPR("pwm_combo1_method = %d\n", bl_pwm->pwm_method); |
| if (bl_pwm->pwm_port == BL_PWM_VS) { |
| BLPR("pwm_combo1_freq = %d x vfreq\n", |
| bl_pwm->pwm_freq); |
| } else { |
| BLPR("pwm_combo1_freq = %uHz\n", |
| bl_pwm->pwm_freq); |
| } |
| BLPR("pwm_combo1_level_max = %u\n", bl_pwm->level_max); |
| BLPR("pwm_combo1_level_min = %u\n", bl_pwm->level_min); |
| BLPR("pwm_combo1_duty_max = %d\n", |
| bl_pwm->pwm_duty_max); |
| BLPR("pwm_combo1_duty_min = %d\n", |
| bl_pwm->pwm_duty_min); |
| BLPR("pwm_combo1_pwm_cnt = %u\n", bl_pwm->pwm_cnt); |
| BLPR("pwm_combo1_pwm_max = %u\n", bl_pwm->pwm_max); |
| BLPR("pwm_combo1_pwm_min = %u\n", bl_pwm->pwm_min); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| #ifdef CONFIG_OF |
| static int bl_config_load_from_dts(struct aml_bl_drv_s *bdrv) |
| { |
| struct bl_config_s *bconf = &bdrv->bconf; |
| const char *str; |
| unsigned int para[10]; |
| char bl_propname[20]; |
| struct device_node *child; |
| struct bl_pwm_config_s *bl_pwm; |
| struct bl_pwm_config_s *pwm_combo0, *pwm_combo1; |
| int val; |
| int ret = 0; |
| |
| /* select backlight by index */ |
| bconf->index = bl_index_lut[bdrv->index]; |
| if (bconf->index == 0xff) { |
| bconf->method = BL_CTRL_MAX; |
| return -1; |
| } |
| sprintf(bl_propname, "backlight_%d", bconf->index); |
| BLPR("[%d]: load: %s\n", bdrv->index, bl_propname); |
| child = of_get_child_by_name(bdrv->dev->of_node, bl_propname); |
| if (!child) { |
| BLERR("[%d]: failed to get %s\n", bdrv->index, bl_propname); |
| return -1; |
| } |
| |
| ret = of_property_read_string(child, "bl_name", &str); |
| if (ret) { |
| BLERR("[%d]: failed to get bl_name\n", bdrv->index); |
| str = "backlight"; |
| } |
| strncpy(bconf->name, str, (BL_NAME_MAX - 1)); |
| |
| ret = of_property_read_u32_array(child, "bl_level_default_uboot_kernel", ¶[0], 2); |
| if (ret) { |
| BLERR("[%d]: failed to get bl_level_default_uboot_kernel\n", bdrv->index); |
| bconf->level_uboot = BL_LEVEL_DEFAULT; |
| bconf->level_default = BL_LEVEL_DEFAULT; |
| } else { |
| bconf->level_uboot = para[0] & BL_LEVEL_MASK; |
| bconf->level_default = para[1] & BL_LEVEL_MASK; |
| |
| bdrv->brightness_bypass = ((para[1] >> BL_POLICY_BRIGHTNESS_BYPASS_BIT) & |
| BL_POLICY_BRIGHTNESS_BYPASS_MASK); |
| if (bdrv->brightness_bypass) { |
| BLPR("[%d]: 0x%x: enable brightness_bypass\n", |
| bdrv->index, para[1]); |
| } |
| bdrv->step_on_flag = ((para[1] >> BL_POLICY_POWER_ON_BIT) & |
| BL_POLICY_POWER_ON_MASK); |
| if (bdrv->step_on_flag) { |
| BLPR("[%d]: 0x%x: bl_step_on_flag: %d\n", |
| bdrv->index, para[1], bdrv->step_on_flag); |
| } |
| } |
| |
| ret = of_property_read_u32_array(child, "bl_level_attr", ¶[0], 4); |
| if (ret) { |
| BLERR("[%d]: failed to get bl_level_attr\n", bdrv->index); |
| bconf->level_min = BL_LEVEL_MIN; |
| bconf->level_max = BL_LEVEL_MAX; |
| bconf->level_mid = BL_LEVEL_MID; |
| bconf->level_mid_mapping = BL_LEVEL_MID_MAPPED; |
| } else { |
| bconf->level_max = para[0]; |
| bconf->level_min = para[1]; |
| bconf->level_mid = para[2]; |
| bconf->level_mid_mapping = para[3]; |
| } |
| |
| ret = of_property_read_u32(child, "bl_ctrl_method", &val); |
| if (ret) { |
| BLERR("[%d]: failed to get bl_ctrl_method\n", bdrv->index); |
| bconf->method = BL_CTRL_MAX; |
| } else { |
| bconf->method = (val >= BL_CTRL_MAX) ? BL_CTRL_MAX : val; |
| } |
| ret = of_property_read_u32_array(child, "bl_power_attr", ¶[0], 5); |
| if (ret) { |
| BLERR("[%d]: failed to get bl_power_attr\n", bdrv->index); |
| bconf->en_gpio = BL_GPIO_MAX; |
| bconf->en_gpio_on = BL_GPIO_OUTPUT_HIGH; |
| bconf->en_gpio_off = BL_GPIO_OUTPUT_LOW; |
| bconf->power_on_delay = 100; |
| bconf->power_off_delay = 30; |
| } else { |
| if (para[0] >= BL_GPIO_NUM_MAX) { |
| bconf->en_gpio = BL_GPIO_MAX; |
| } else { |
| bconf->en_gpio = para[0]; |
| bl_gpio_probe(bdrv, bconf->en_gpio); |
| } |
| bconf->en_gpio_on = para[1]; |
| bconf->en_gpio_off = para[2]; |
| bconf->power_on_delay = para[3]; |
| bconf->power_off_delay = para[4]; |
| } |
| ret = of_property_read_u32(child, "en_sequence_reverse", &val); |
| if (ret) { |
| bconf->en_sequence_reverse = 0; |
| } else { |
| bconf->en_sequence_reverse = val; |
| BLPR("[%d]: find en_sequence_reverse: %d\n", bdrv->index, val); |
| } |
| |
| ret = of_property_read_u32(child, "bl_ldim_region_row_col", ¶[0]); |
| if (ret) { |
| ret = of_property_read_u32(child, "bl_ldim_zone_row_col", ¶[0]); |
| if (ret == 0) |
| bconf->ldim_flag = 1; |
| } else { |
| bconf->ldim_flag = 1; |
| } |
| |
| switch (bconf->method) { |
| case BL_CTRL_PWM: |
| bconf->bl_pwm = kzalloc(sizeof(*bconf->bl_pwm), GFP_KERNEL); |
| if (!bconf->bl_pwm) |
| return -1; |
| |
| bl_pwm = bconf->bl_pwm; |
| bl_pwm->index = 0; |
| |
| bl_pwm->level_max = bconf->level_max; |
| bl_pwm->level_min = bconf->level_min; |
| |
| ret = of_property_read_string(child, "bl_pwm_port", &str); |
| if (ret) { |
| BLERR("[%d]: failed to get bl_pwm_port\n", bdrv->index); |
| bl_pwm->pwm_port = BL_PWM_MAX; |
| } else { |
| bl_pwm->pwm_port = bl_pwm_str_to_num(str); |
| BLPR("[%d]: bl pwm_port: %s(0x%x)\n", |
| bdrv->index, str, bl_pwm->pwm_port); |
| } |
| ret = of_property_read_u32_array(child, "bl_pwm_attr", ¶[0], 4); |
| if (ret) { |
| BLERR("[%d]: failed to get bl_pwm_attr\n", bdrv->index); |
| bl_pwm->pwm_method = BL_PWM_POSITIVE; |
| if (bl_pwm->pwm_port == BL_PWM_VS) |
| bl_pwm->pwm_freq = BL_FREQ_VS_DEFAULT; |
| else |
| bl_pwm->pwm_freq = BL_FREQ_DEFAULT; |
| bl_pwm->pwm_duty_max = 80; |
| bl_pwm->pwm_duty_min = 20; |
| } else { |
| bl_pwm->pwm_method = para[0]; |
| bl_pwm->pwm_freq = para[1]; |
| bl_pwm->pwm_duty_max = para[2]; |
| bl_pwm->pwm_duty_min = para[3]; |
| } |
| if (bl_pwm->pwm_port == BL_PWM_VS) { |
| if (bl_pwm->pwm_freq > 4) { |
| BLERR("[%d]: bl_pwm_vs wrong freq %d\n", |
| bdrv->index, bl_pwm->pwm_freq); |
| bl_pwm->pwm_freq = BL_FREQ_VS_DEFAULT; |
| } |
| } else { |
| if (bl_pwm->pwm_freq > XTAL_HALF_FREQ_HZ) |
| bl_pwm->pwm_freq = XTAL_HALF_FREQ_HZ; |
| if (bl_pwm->pwm_freq < 50) |
| bl_pwm->pwm_freq = 50; |
| } |
| ret = of_property_read_u32_array(child, "bl_pwm_power", ¶[0], 4); |
| if (ret) { |
| BLERR("[%d]: failed to get bl_pwm_power\n", |
| bdrv->index); |
| bconf->pwm_on_delay = 0; |
| bconf->pwm_off_delay = 0; |
| } else { |
| bconf->pwm_on_delay = para[2]; |
| bconf->pwm_off_delay = para[3]; |
| } |
| |
| bl_pwm->pwm_duty = bl_pwm->pwm_duty_min; |
| /* init pwm config */ |
| bl_pwm_config_init(bl_pwm); |
| break; |
| case BL_CTRL_PWM_COMBO: |
| bconf->bl_pwm_combo0 = kzalloc(sizeof(*bconf->bl_pwm_combo0), GFP_KERNEL); |
| if (!bconf->bl_pwm_combo0) |
| return -1; |
| bconf->bl_pwm_combo1 = kzalloc(sizeof(*bconf->bl_pwm_combo1), GFP_KERNEL); |
| if (!bconf->bl_pwm_combo1) |
| return -1; |
| |
| pwm_combo0 = bconf->bl_pwm_combo0; |
| pwm_combo1 = bconf->bl_pwm_combo1; |
| |
| pwm_combo0->index = 0; |
| pwm_combo1->index = 1; |
| |
| ret = of_property_read_string_index(child, "bl_pwm_combo_port", 0, &str); |
| if (ret) { |
| BLERR("[%d]: failed to get bl_pwm_combo_port\n", bdrv->index); |
| pwm_combo0->pwm_port = BL_PWM_MAX; |
| } else { |
| pwm_combo0->pwm_port = bl_pwm_str_to_num(str); |
| } |
| ret = of_property_read_string_index(child, "bl_pwm_combo_port", 1, &str); |
| if (ret) { |
| BLERR("[%d]: failed to get bl_pwm_combo_port\n", bdrv->index); |
| pwm_combo1->pwm_port = BL_PWM_MAX; |
| } else { |
| pwm_combo1->pwm_port = bl_pwm_str_to_num(str); |
| } |
| BLPR("[%d]: pwm_combo_port: %s(0x%x), %s(0x%x)\n", |
| bdrv->index, |
| bl_pwm_num_to_str(pwm_combo0->pwm_port), |
| pwm_combo0->pwm_port, |
| bl_pwm_num_to_str(pwm_combo1->pwm_port), |
| pwm_combo1->pwm_port); |
| ret = of_property_read_u32_array(child, "bl_pwm_combo_level_mapping", |
| ¶[0], 4); |
| if (ret) { |
| BLERR("[%d]: failed to get bl_pwm_combo_level_mapping\n", |
| bdrv->index); |
| pwm_combo0->level_max = BL_LEVEL_MAX; |
| pwm_combo0->level_min = BL_LEVEL_MID; |
| pwm_combo1->level_max = BL_LEVEL_MID; |
| pwm_combo1->level_min = BL_LEVEL_MIN; |
| } else { |
| pwm_combo0->level_max = para[0]; |
| pwm_combo0->level_min = para[1]; |
| pwm_combo1->level_max = para[2]; |
| pwm_combo1->level_min = para[3]; |
| } |
| ret = of_property_read_u32_array(child, "bl_pwm_combo_attr", ¶[0], 8); |
| if (ret) { |
| BLERR("[%d]: failed to get bl_pwm_combo_attr\n", bdrv->index); |
| pwm_combo0->pwm_method = BL_PWM_POSITIVE; |
| if (pwm_combo0->pwm_port == BL_PWM_VS) |
| pwm_combo0->pwm_freq = BL_FREQ_VS_DEFAULT; |
| else |
| pwm_combo0->pwm_freq = BL_FREQ_DEFAULT; |
| pwm_combo0->pwm_duty_max = 80; |
| pwm_combo0->pwm_duty_min = 20; |
| pwm_combo1->pwm_method = BL_PWM_NEGATIVE; |
| if (pwm_combo1->pwm_port == BL_PWM_VS) |
| pwm_combo1->pwm_freq = BL_FREQ_VS_DEFAULT; |
| else |
| pwm_combo1->pwm_freq = BL_FREQ_DEFAULT; |
| pwm_combo1->pwm_duty_max = 80; |
| pwm_combo1->pwm_duty_min = 20; |
| } else { |
| pwm_combo0->pwm_method = para[0]; |
| pwm_combo0->pwm_freq = para[1]; |
| pwm_combo0->pwm_duty_max = para[2]; |
| pwm_combo0->pwm_duty_min = para[3]; |
| pwm_combo1->pwm_method = para[4]; |
| pwm_combo1->pwm_freq = para[5]; |
| pwm_combo1->pwm_duty_max = para[6]; |
| pwm_combo1->pwm_duty_min = para[7]; |
| } |
| if (pwm_combo0->pwm_port == BL_PWM_VS) { |
| if (pwm_combo0->pwm_freq > 4) { |
| BLERR("[%d]: bl_pwm_0_vs wrong freq %d\n", |
| bdrv->index, pwm_combo0->pwm_freq); |
| pwm_combo0->pwm_freq = BL_FREQ_VS_DEFAULT; |
| } |
| } else { |
| if (pwm_combo0->pwm_freq > XTAL_HALF_FREQ_HZ) |
| pwm_combo0->pwm_freq = XTAL_HALF_FREQ_HZ; |
| if (pwm_combo0->pwm_freq < 50) |
| pwm_combo0->pwm_freq = 50; |
| } |
| if (pwm_combo1->pwm_port == BL_PWM_VS) { |
| if (pwm_combo1->pwm_freq > 4) { |
| BLERR("[%d]: bl_pwm_1_vs wrong freq %d\n", |
| bdrv->index, pwm_combo1->pwm_freq); |
| pwm_combo1->pwm_freq = BL_FREQ_VS_DEFAULT; |
| } |
| } else { |
| if (pwm_combo1->pwm_freq > XTAL_HALF_FREQ_HZ) |
| pwm_combo1->pwm_freq = XTAL_HALF_FREQ_HZ; |
| if (pwm_combo1->pwm_freq < 50) |
| pwm_combo1->pwm_freq = 50; |
| } |
| ret = of_property_read_u32_array(child, "bl_pwm_combo_power", ¶[0], 6); |
| if (ret) { |
| BLERR("[%d]: failed to get bl_pwm_combo_power\n", bdrv->index); |
| bconf->pwm_on_delay = 0; |
| bconf->pwm_off_delay = 0; |
| } else { |
| bconf->pwm_on_delay = para[4]; |
| bconf->pwm_off_delay = para[5]; |
| } |
| |
| pwm_combo0->pwm_duty = pwm_combo0->pwm_duty_min; |
| pwm_combo1->pwm_duty = pwm_combo1->pwm_duty_min; |
| /* init pwm config */ |
| bl_pwm_config_init(pwm_combo0); |
| bl_pwm_config_init(pwm_combo1); |
| break; |
| #ifdef CONFIG_AMLOGIC_BL_LDIM |
| case BL_CTRL_LOCAL_DIMMING: |
| bconf->ldim_flag = 1; |
| break; |
| #endif |
| #ifdef CONFIG_AMLOGIC_BL_EXTERN |
| case BL_CTRL_EXTERN: |
| /* get bl_extern_index from dts */ |
| ret = of_property_read_u32(child, "bl_extern_index", ¶[0]); |
| if (ret) { |
| BLERR("[%d]: failed to get bl_extern_index\n", bdrv->index); |
| } else { |
| bconf->bl_extern_index = para[0]; |
| bl_extern_dev_index_add(bdrv->index, para[0]); |
| BLPR("[%d]: get bl_extern_index = %d\n", |
| bdrv->index, bconf->bl_extern_index); |
| } |
| break; |
| #endif |
| default: |
| break; |
| } |
| |
| #ifdef CONFIG_AMLOGIC_BL_LDIM |
| if (bconf->ldim_flag) { |
| ret = aml_ldim_get_config_dts(child); |
| if (ret < 0) |
| return -1; |
| } |
| #endif |
| |
| return 0; |
| } |
| #endif |
| |
| static int bl_config_load_from_unifykey(struct aml_bl_drv_s *bdrv, char *key_name) |
| { |
| struct bl_config_s *bconf = &bdrv->bconf; |
| unsigned char *para; |
| int key_len, len; |
| unsigned char *p; |
| const char *str; |
| unsigned char temp; |
| struct aml_lcd_unifykey_header_s bl_header; |
| struct bl_pwm_config_s *bl_pwm; |
| struct bl_pwm_config_s *pwm_combo0, *pwm_combo1; |
| unsigned int level; |
| int ret; |
| |
| if (!key_name) |
| return -1; |
| |
| key_len = LCD_UKEY_BL_SIZE; |
| para = kcalloc(key_len, (sizeof(unsigned char)), GFP_KERNEL); |
| if (!para) |
| return -1; |
| |
| ret = lcd_unifykey_get(key_name, para, &key_len); |
| if (ret < 0) { |
| kfree(para); |
| return -1; |
| } |
| |
| /* step 1: check header */ |
| len = LCD_UKEY_HEAD_SIZE; |
| ret = lcd_unifykey_len_check(key_len, len); |
| if (ret < 0) { |
| BLERR("[%d]: unifykey header length is incorrect\n", bdrv->index); |
| kfree(para); |
| return -1; |
| } |
| |
| lcd_unifykey_header_check(para, &bl_header); |
| BLPR("[%d]: unifykey version: 0x%04x\n", bdrv->index, bl_header.version); |
| switch (bl_header.version) { |
| case 2: |
| len = 10 + 30 + 12 + 8 + 32 + 10; |
| break; |
| default: |
| len = 10 + 30 + 12 + 8 + 32; |
| break; |
| } |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) { |
| BLPR("unifykey header:\n"); |
| BLPR("crc32 = 0x%08x\n", bl_header.crc32); |
| BLPR("data_len = %d\n", bl_header.data_len); |
| BLPR("reserved = 0x%04x\n", bl_header.reserved); |
| } |
| |
| /* step 2: check backlight parameters */ |
| ret = lcd_unifykey_len_check(key_len, len); |
| if (ret < 0) { |
| BLERR("[%d]: unifykey length is incorrect\n", bdrv->index); |
| kfree(para); |
| return -1; |
| } |
| |
| /* basic: 30byte */ |
| p = para; |
| str = (const char *)(p + LCD_UKEY_HEAD_SIZE); |
| strncpy(bconf->name, str, (BL_NAME_MAX - 1)); |
| bconf->index = 0; |
| |
| /* level: 6byte */ |
| bconf->level_uboot = (*(p + LCD_UKEY_BL_LEVEL_UBOOT) | |
| ((*(p + LCD_UKEY_BL_LEVEL_UBOOT + 1)) << 8)); |
| level = (*(p + LCD_UKEY_BL_LEVEL_KERNEL) | |
| ((*(p + LCD_UKEY_BL_LEVEL_KERNEL + 1)) << 8)); |
| bconf->level_max = (*(p + LCD_UKEY_BL_LEVEL_MAX) | |
| ((*(p + LCD_UKEY_BL_LEVEL_MAX + 1)) << 8)); |
| bconf->level_min = (*(p + LCD_UKEY_BL_LEVEL_MIN) | |
| ((*(p + LCD_UKEY_BL_LEVEL_MIN + 1)) << 8)); |
| bconf->level_mid = (*(p + LCD_UKEY_BL_LEVEL_MID) | |
| ((*(p + LCD_UKEY_BL_LEVEL_MID + 1)) << 8)); |
| bconf->level_mid_mapping = (*(p + LCD_UKEY_BL_LEVEL_MID_MAP) | |
| ((*(p + LCD_UKEY_BL_LEVEL_MID_MAP + 1)) << 8)); |
| |
| bconf->level_default = level & BL_LEVEL_MASK; |
| bdrv->brightness_bypass = |
| ((level >> BL_POLICY_BRIGHTNESS_BYPASS_BIT) & |
| BL_POLICY_BRIGHTNESS_BYPASS_MASK); |
| if (bdrv->brightness_bypass) { |
| BLPR("[%d]: 0x%x: enable brightness_bypass\n", |
| bdrv->index, level); |
| } |
| bdrv->step_on_flag = ((level >> BL_POLICY_POWER_ON_BIT) & |
| BL_POLICY_POWER_ON_MASK); |
| if (bdrv->step_on_flag) { |
| BLPR("[%d]: 0x%x: bl_step_on_flag: %d\n", |
| bdrv->index, level, bdrv->step_on_flag); |
| } |
| |
| /* method: 8byte */ |
| temp = *(p + LCD_UKEY_BL_METHOD); |
| bconf->method = (temp >= BL_CTRL_MAX) ? BL_CTRL_MAX : temp; |
| |
| if (*(p + LCD_UKEY_BL_EN_GPIO) >= BL_GPIO_NUM_MAX) { |
| bconf->en_gpio = BL_GPIO_MAX; |
| } else { |
| bconf->en_gpio = *(p + LCD_UKEY_BL_EN_GPIO); |
| bl_gpio_probe(bdrv, bconf->en_gpio); |
| } |
| bconf->en_gpio_on = *(p + LCD_UKEY_BL_EN_GPIO_ON); |
| bconf->en_gpio_off = *(p + LCD_UKEY_BL_EN_GPIO_OFF); |
| bconf->power_on_delay = (*(p + LCD_UKEY_BL_ON_DELAY) | |
| ((*(p + LCD_UKEY_BL_ON_DELAY + 1)) << 8)); |
| bconf->power_off_delay = (*(p + LCD_UKEY_BL_OFF_DELAY) | |
| ((*(p + LCD_UKEY_BL_OFF_DELAY + 1)) << 8)); |
| |
| if (bl_header.version == 2) { |
| bconf->en_sequence_reverse = (*(p + LCD_UKEY_BL_CUST_VAL_0) | |
| ((*(p + LCD_UKEY_BL_CUST_VAL_0 + 1)) << 8)); |
| } else { |
| bconf->en_sequence_reverse = 0; |
| } |
| |
| /* pwm: 24byte */ |
| switch (bconf->method) { |
| case BL_CTRL_PWM: |
| bconf->bl_pwm = kzalloc(sizeof(*bconf->bl_pwm), GFP_KERNEL); |
| if (!bconf->bl_pwm) { |
| kfree(para); |
| return -1; |
| } |
| bl_pwm = bconf->bl_pwm; |
| bl_pwm->index = 0; |
| |
| bl_pwm->level_max = bconf->level_max; |
| bl_pwm->level_min = bconf->level_min; |
| |
| bconf->pwm_on_delay = (*(p + LCD_UKEY_BL_PWM_ON_DELAY) | |
| ((*(p + LCD_UKEY_BL_PWM_ON_DELAY + 1)) << 8)); |
| bconf->pwm_off_delay = (*(p + LCD_UKEY_BL_PWM_OFF_DELAY) | |
| ((*(p + LCD_UKEY_BL_PWM_OFF_DELAY + 1)) << 8)); |
| bl_pwm->pwm_method = *(p + LCD_UKEY_BL_PWM_METHOD); |
| bl_pwm->pwm_port = *(p + LCD_UKEY_BL_PWM_PORT); |
| bl_pwm->pwm_freq = (*(p + LCD_UKEY_BL_PWM_FREQ) | |
| ((*(p + LCD_UKEY_BL_PWM_FREQ + 1)) << 8) | |
| ((*(p + LCD_UKEY_BL_PWM_FREQ + 2)) << 8) | |
| ((*(p + LCD_UKEY_BL_PWM_FREQ + 3)) << 8)); |
| if (bl_pwm->pwm_port == BL_PWM_VS) { |
| if (bl_pwm->pwm_freq > 4) { |
| BLERR("bl_pwm_vs wrong freq %d\n", bl_pwm->pwm_freq); |
| bl_pwm->pwm_freq = BL_FREQ_VS_DEFAULT; |
| } |
| } else { |
| if (bl_pwm->pwm_freq > XTAL_HALF_FREQ_HZ) |
| bl_pwm->pwm_freq = XTAL_HALF_FREQ_HZ; |
| } |
| bl_pwm->pwm_duty_max = *(p + LCD_UKEY_BL_PWM_DUTY_MAX); |
| bl_pwm->pwm_duty_min = *(p + LCD_UKEY_BL_PWM_DUTY_MIN); |
| |
| bl_pwm->pwm_duty = bl_pwm->pwm_duty_min; |
| bl_pwm_config_init(bl_pwm); |
| break; |
| case BL_CTRL_PWM_COMBO: |
| bconf->bl_pwm_combo0 = kzalloc(sizeof(*bconf->bl_pwm_combo0), GFP_KERNEL); |
| if (!bconf->bl_pwm_combo0) { |
| kfree(para); |
| return -1; |
| } |
| bconf->bl_pwm_combo1 = kzalloc(sizeof(*bconf->bl_pwm_combo1), GFP_KERNEL); |
| if (!bconf->bl_pwm_combo1) { |
| kfree(para); |
| return -1; |
| } |
| pwm_combo0 = bconf->bl_pwm_combo0; |
| pwm_combo1 = bconf->bl_pwm_combo1; |
| pwm_combo0->index = 0; |
| pwm_combo1->index = 1; |
| |
| bconf->pwm_on_delay = (*(p + LCD_UKEY_BL_PWM_ON_DELAY) | |
| ((*(p + LCD_UKEY_BL_PWM_ON_DELAY + 1)) << 8)); |
| bconf->pwm_off_delay = (*(p + LCD_UKEY_BL_PWM_OFF_DELAY) | |
| ((*(p + LCD_UKEY_BL_PWM_OFF_DELAY + 1)) << 8)); |
| |
| pwm_combo0->pwm_method = *(p + LCD_UKEY_BL_PWM_METHOD); |
| pwm_combo0->pwm_port = *(p + LCD_UKEY_BL_PWM_PORT); |
| pwm_combo0->pwm_freq = (*(p + LCD_UKEY_BL_PWM_FREQ) | |
| ((*(p + LCD_UKEY_BL_PWM_FREQ + 1)) << 8) | |
| ((*(p + LCD_UKEY_BL_PWM_FREQ + 2)) << 8) | |
| ((*(p + LCD_UKEY_BL_PWM_FREQ + 3)) << 8)); |
| if (pwm_combo0->pwm_port == BL_PWM_VS) { |
| if (pwm_combo0->pwm_freq > 4) { |
| BLERR("bl_pwm_0_vs wrong freq %d\n", pwm_combo0->pwm_freq); |
| pwm_combo0->pwm_freq = BL_FREQ_VS_DEFAULT; |
| } |
| } else { |
| if (pwm_combo0->pwm_freq > XTAL_HALF_FREQ_HZ) |
| pwm_combo0->pwm_freq = XTAL_HALF_FREQ_HZ; |
| } |
| pwm_combo0->pwm_duty_max = *(p + LCD_UKEY_BL_PWM_DUTY_MAX); |
| pwm_combo0->pwm_duty_min = *(p + LCD_UKEY_BL_PWM_DUTY_MIN); |
| |
| pwm_combo1->pwm_method = *(p + LCD_UKEY_BL_PWM2_METHOD); |
| pwm_combo1->pwm_port = *(p + LCD_UKEY_BL_PWM2_PORT); |
| pwm_combo1->pwm_freq = (*(p + LCD_UKEY_BL_PWM2_FREQ) | |
| ((*(p + LCD_UKEY_BL_PWM2_FREQ + 1)) << 8) | |
| ((*(p + LCD_UKEY_BL_PWM2_FREQ + 2)) << 8) | |
| ((*(p + LCD_UKEY_BL_PWM2_FREQ + 3)) << 8)); |
| if (pwm_combo1->pwm_port == BL_PWM_VS) { |
| if (pwm_combo1->pwm_freq > 4) { |
| BLERR("bl_pwm_1_vs wrong freq %d\n", pwm_combo1->pwm_freq); |
| pwm_combo1->pwm_freq = BL_FREQ_VS_DEFAULT; |
| } |
| } else { |
| if (pwm_combo1->pwm_freq > XTAL_HALF_FREQ_HZ) |
| pwm_combo1->pwm_freq = XTAL_HALF_FREQ_HZ; |
| } |
| pwm_combo1->pwm_duty_max = *(p + LCD_UKEY_BL_PWM2_DUTY_MAX); |
| pwm_combo1->pwm_duty_min = *(p + LCD_UKEY_BL_PWM2_DUTY_MIN); |
| |
| pwm_combo0->level_max = (*(p + LCD_UKEY_BL_PWM_LEVEL_MAX) | |
| ((*(p + LCD_UKEY_BL_PWM_LEVEL_MAX + 1)) << 8)); |
| pwm_combo0->level_min = (*(p + LCD_UKEY_BL_PWM_LEVEL_MIN) | |
| ((*(p + LCD_UKEY_BL_PWM_LEVEL_MIN + 1)) << 8)); |
| pwm_combo1->level_max = (*(p + LCD_UKEY_BL_PWM2_LEVEL_MAX) | |
| ((*(p + LCD_UKEY_BL_PWM2_LEVEL_MAX + 1)) << 8)); |
| pwm_combo1->level_min = (*(p + LCD_UKEY_BL_PWM2_LEVEL_MIN) | |
| ((*(p + LCD_UKEY_BL_PWM2_LEVEL_MIN + 1)) << 8)); |
| |
| pwm_combo0->pwm_duty = pwm_combo0->pwm_duty_min; |
| pwm_combo1->pwm_duty = pwm_combo1->pwm_duty_min; |
| bl_pwm_config_init(pwm_combo0); |
| bl_pwm_config_init(pwm_combo1); |
| break; |
| #ifdef CONFIG_AMLOGIC_BL_LDIM |
| case BL_CTRL_LOCAL_DIMMING: |
| bconf->ldim_flag = 1; |
| break; |
| #endif |
| default: |
| break; |
| } |
| |
| #ifdef CONFIG_AMLOGIC_BL_LDIM |
| if (bconf->ldim_flag) { |
| ret = aml_ldim_get_config_unifykey(para); |
| if (ret < 0) { |
| kfree(para); |
| return -1; |
| } |
| } |
| #endif |
| |
| kfree(para); |
| return 0; |
| } |
| |
| static int bl_config_load(struct aml_bl_drv_s *bdrv, struct platform_device *pdev) |
| { |
| unsigned int temp; |
| char key_name[15]; |
| int load_id = 0, i; |
| bool is_init; |
| phandle pwm_phandle; |
| int ret = 0; |
| |
| if (!bdrv->dev->of_node) { |
| BLERR("no backlight[%d] of_node exist\n", bdrv->index); |
| return -1; |
| } |
| |
| ret = of_property_read_u32(bdrv->dev->of_node, "key_valid", &temp); |
| if (ret) { |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) |
| BLPR("[%d]: failed to get key_valid\n", bdrv->index); |
| temp = 0; |
| } |
| bdrv->key_valid = temp; |
| BLPR("[%d]: key_valid: %d\n", bdrv->index, bdrv->key_valid); |
| |
| if (bdrv->key_valid) { |
| if (bdrv->index == 0) |
| sprintf(key_name, "backlight"); |
| else |
| sprintf(key_name, "backlight%d", bdrv->index); |
| |
| is_init = lcd_unifykey_init_get(); |
| i = 0; |
| while (!is_init) { |
| if (i++ >= LCD_UNIFYKEY_WAIT_TIMEOUT) |
| break; |
| lcd_delay_ms(LCD_UNIFYKEY_RETRY_INTERVAL); |
| is_init = lcd_unifykey_init_get(); |
| } |
| if (is_init) { |
| ret = lcd_unifykey_check(key_name); |
| if (ret < 0) |
| load_id = 0; |
| else |
| load_id = 1; |
| } else { |
| load_id = 0; |
| BLERR("[%d]: key_init_flag=%d\n", bdrv->index, is_init); |
| } |
| } |
| if (load_id) { |
| BLPR("[%d]: %s from unifykey\n", bdrv->index, __func__); |
| bdrv->config_load = 1; |
| ret = bl_config_load_from_unifykey(bdrv, key_name); |
| } else { |
| #ifdef CONFIG_OF |
| BLPR("[%d]: %s from dts\n", bdrv->index, __func__); |
| bdrv->config_load = 0; |
| ret = bl_config_load_from_dts(bdrv); |
| #endif |
| } |
| if (ret) |
| return -1; |
| |
| if (bl_level[bdrv->index]) |
| bdrv->bconf.level_uboot = bl_level[bdrv->index]; |
| bdrv->level_init_on = (bdrv->bconf.level_uboot > 0) ? |
| bdrv->bconf.level_uboot : BL_LEVEL_DEFAULT; |
| |
| bl_config_print(bdrv); |
| |
| #ifdef CONFIG_AMLOGIC_BL_LDIM |
| if (bdrv->bconf.ldim_flag) |
| aml_ldim_probe(pdev); |
| #endif |
| |
| bdrv->res_vsync_irq[0] = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "vsync"); |
| if (!bdrv->res_vsync_irq[0]) { |
| ret = -ENODEV; |
| BLERR("[%d]: bl_vsync_irq[0] resource error\n", bdrv->index); |
| return -1; |
| } |
| |
| switch (bdrv->bconf.method) { |
| case BL_CTRL_PWM: |
| if (bdrv->bconf.bl_pwm->pwm_port >= BL_PWM_VS) |
| break; |
| ret = of_property_read_u32(bdrv->dev->of_node, "bl_pwm_config", &pwm_phandle); |
| if (ret) { |
| BLERR("%s: not match bl_pwm_config node\n", __func__); |
| return -1; |
| } |
| ret = bl_pwm_channel_register(bdrv->dev, pwm_phandle, bdrv->bconf.bl_pwm); |
| if (ret) |
| return -1; |
| break; |
| case BL_CTRL_PWM_COMBO: |
| ret = of_property_read_u32(bdrv->dev->of_node, "bl_pwm_config", &pwm_phandle); |
| if (ret) { |
| BLERR("%s: not match bl_pwm_config node\n", __func__); |
| return -1; |
| } |
| if (bdrv->bconf.bl_pwm_combo0->pwm_port < BL_PWM_VS) { |
| ret = bl_pwm_channel_register(bdrv->dev, pwm_phandle, |
| bdrv->bconf.bl_pwm_combo0); |
| if (ret) |
| return -1; |
| } |
| if (bdrv->bconf.bl_pwm_combo1->pwm_port < BL_PWM_VS) { |
| ret = bl_pwm_channel_register(bdrv->dev, pwm_phandle, |
| bdrv->bconf.bl_pwm_combo1); |
| if (ret) |
| return -1; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| /* lcd notify */ |
| static void bl_on_function(struct aml_bl_drv_s *bdrv) |
| { |
| struct bl_config_s *bconf = &bdrv->bconf; |
| |
| mutex_lock(&bl_level_mutex); |
| |
| /* lcd power on backlight flag */ |
| bdrv->state |= BL_STATE_LCD_ON; |
| BLPR("%s: bl_step_on_flag=%d, bl_level=%u, state=0x%x\n", |
| __func__, bdrv->step_on_flag, bdrv->level, bdrv->state); |
| |
| bdrv->level_brightness = bl_brightness_level_map(bdrv, |
| bdrv->bldev->props.brightness); |
| |
| bdrv->state |= BL_STATE_BL_INIT_ON; |
| switch (bdrv->step_on_flag) { |
| case 1: |
| BLPR("bl_step_on level: %d\n", bconf->level_default); |
| aml_bl_init_level(bdrv, bconf->level_default); |
| msleep(120); |
| if (bdrv->brightness_bypass) { |
| switch (bconf->method) { |
| case BL_CTRL_PWM: |
| bconf->bl_pwm->pwm_duty = |
| bconf->bl_pwm->pwm_duty_save; |
| bl_pwm_set_duty(bdrv, bconf->bl_pwm); |
| break; |
| case BL_CTRL_PWM_COMBO: |
| bconf->bl_pwm_combo0->pwm_duty = |
| bconf->bl_pwm_combo0->pwm_duty_save; |
| bconf->bl_pwm_combo1->pwm_duty = |
| bconf->bl_pwm_combo1->pwm_duty_save; |
| bl_pwm_set_duty(bdrv, bconf->bl_pwm_combo0); |
| bl_pwm_set_duty(bdrv, bconf->bl_pwm_combo1); |
| break; |
| default: |
| break; |
| } |
| } else { |
| BLPR("bl_on level: %d\n", bdrv->level_brightness); |
| aml_bl_init_level(bdrv, bdrv->level_brightness); |
| } |
| break; |
| case 2: |
| BLPR("bl_step_on level: %d\n", bconf->level_uboot); |
| aml_bl_init_level(bdrv, bconf->level_uboot); |
| msleep(120); |
| if (bdrv->brightness_bypass) { |
| switch (bconf->method) { |
| case BL_CTRL_PWM: |
| bconf->bl_pwm->pwm_duty = |
| bconf->bl_pwm->pwm_duty_save; |
| bl_pwm_set_duty(bdrv, bconf->bl_pwm); |
| break; |
| case BL_CTRL_PWM_COMBO: |
| bconf->bl_pwm_combo0->pwm_duty = |
| bconf->bl_pwm_combo0->pwm_duty_save; |
| bconf->bl_pwm_combo1->pwm_duty = |
| bconf->bl_pwm_combo1->pwm_duty_save; |
| bl_pwm_set_duty(bdrv, bconf->bl_pwm_combo0); |
| bl_pwm_set_duty(bdrv, bconf->bl_pwm_combo1); |
| break; |
| default: |
| break; |
| } |
| } else { |
| BLPR("bl_on level: %d\n", bdrv->level_brightness); |
| aml_bl_init_level(bdrv, bdrv->level_brightness); |
| } |
| break; |
| default: |
| if (bdrv->brightness_bypass) { |
| if ((bdrv->state & BL_STATE_BL_ON) == 0) |
| bl_power_on(bdrv); |
| } else { |
| aml_bl_init_level(bdrv, bdrv->level_brightness); |
| } |
| break; |
| } |
| bdrv->state &= ~(BL_STATE_BL_INIT_ON); |
| |
| mutex_unlock(&bl_level_mutex); |
| } |
| |
| static void bl_delayd_on(struct work_struct *p_work) |
| { |
| struct delayed_work *d_work; |
| struct aml_bl_drv_s *bdrv; |
| |
| d_work = container_of(p_work, struct delayed_work, work); |
| bdrv = container_of(d_work, struct aml_bl_drv_s, delayed_on_work); |
| |
| if (bdrv->probe_done == 0) |
| return; |
| if (bdrv->on_request == 0) |
| return; |
| |
| bl_on_function(bdrv); |
| } |
| |
| static int bl_lcd_on_notifier(struct notifier_block *nb, |
| unsigned long event, void *data) |
| { |
| struct aml_lcd_drv_s *pdrv = (struct aml_lcd_drv_s *)data; |
| struct aml_bl_drv_s *bdrv; |
| |
| if ((event & LCD_EVENT_BL_ON) == 0) |
| return NOTIFY_DONE; |
| |
| if (!pdrv) |
| return NOTIFY_DONE; |
| bdrv = aml_bl_get_driver(pdrv->index); |
| if (aml_bl_check_driver(bdrv)) |
| return NOTIFY_DONE; |
| if (bdrv->probe_done == 0) |
| return NOTIFY_DONE; |
| |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) |
| BLPR("[%d]: %s: 0x%lx\n", bdrv->index, __func__, event); |
| |
| bdrv->on_request = 1; |
| /* lcd power on sequence control */ |
| if (bdrv->bconf.method < BL_CTRL_MAX) { |
| #ifdef BL_POWER_ON_DELAY_WORK |
| lcd_queue_delayed_on_work(&bdrv->delayed_on_work, |
| bdrv->bconf.power_on_delay); |
| #else |
| lcd_delay_ms(bdrv->bconf.power_on_delay); |
| bl_on_function(bdrv); |
| #endif |
| } else { |
| BLERR("[%d]: wrong backlight control method\n", bdrv->index); |
| } |
| |
| return NOTIFY_OK; |
| } |
| |
| static int bl_lcd_off_notifier(struct notifier_block *nb, |
| unsigned long event, void *data) |
| { |
| struct aml_lcd_drv_s *pdrv = (struct aml_lcd_drv_s *)data; |
| struct aml_bl_drv_s *bdrv; |
| |
| if ((event & LCD_EVENT_BL_OFF) == 0) |
| return NOTIFY_DONE; |
| |
| if (!pdrv) |
| return NOTIFY_DONE; |
| bdrv = aml_bl_get_driver(pdrv->index); |
| if (aml_bl_check_driver(bdrv)) |
| return NOTIFY_DONE; |
| if (bdrv->probe_done == 0) |
| return NOTIFY_DONE; |
| |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) |
| BLPR("[%d]: %s: 0x%lx\n", bdrv->index, __func__, event); |
| |
| bdrv->on_request = 0; |
| bdrv->state &= ~BL_STATE_LCD_ON; |
| mutex_lock(&bl_level_mutex); |
| bdrv->state |= BL_STATE_BL_INIT_ON; |
| if (bdrv->state & BL_STATE_BL_ON) |
| bl_power_off(bdrv); |
| bdrv->state &= ~BL_STATE_BL_INIT_ON; |
| mutex_unlock(&bl_level_mutex); |
| |
| return NOTIFY_OK; |
| } |
| |
| static struct notifier_block bl_lcd_on_nb = { |
| .notifier_call = bl_lcd_on_notifier, |
| .priority = LCD_PRIORITY_POWER_BL_ON, |
| }; |
| |
| static struct notifier_block bl_lcd_off_nb = { |
| .notifier_call = bl_lcd_off_notifier, |
| .priority = LCD_PRIORITY_POWER_BL_OFF, |
| }; |
| |
| static inline int bl_pwm_vs_lcd_update(struct aml_bl_drv_s *bdrv, |
| struct bl_pwm_config_s *bl_pwm) |
| { |
| unsigned int cnt; |
| |
| if (!bl_pwm) { |
| BLERR("[%d]: %s: bl_pwm is null\n", bdrv->index, __func__); |
| return 0; |
| } |
| |
| cnt = lcd_vcbus_read(ENCL_VIDEO_MAX_LNCNT) + 1; |
| if (cnt == bl_pwm->pwm_cnt) |
| return 0; |
| |
| mutex_lock(&bl_level_mutex); |
| bl_pwm_config_init(bl_pwm); |
| |
| if (bdrv->state & BL_STATE_GD_EN) { |
| mutex_unlock(&bl_level_mutex); |
| return 0; |
| } |
| |
| if (bdrv->brightness_bypass) |
| bl_pwm_set_duty(bdrv, bl_pwm); |
| else |
| aml_bl_set_level(bdrv, bdrv->bldev->props.brightness); |
| mutex_unlock(&bl_level_mutex); |
| |
| return 0; |
| } |
| |
| static int bl_lcd_update_notifier(struct notifier_block *nb, |
| unsigned long event, void *data) |
| { |
| struct aml_lcd_drv_s *pdrv = (struct aml_lcd_drv_s *)data; |
| struct aml_bl_drv_s *bdrv; |
| struct bl_metrics_config_s *bl_metrics_conf; |
| struct bl_pwm_config_s *bl_pwm = NULL; |
| unsigned int frame_rate; |
| #ifdef CONFIG_AMLOGIC_BL_LDIM |
| struct aml_ldim_driver_s *ldim_drv = aml_ldim_get_driver(); |
| #endif |
| |
| /* If we aren't interested in this event, skip it immediately */ |
| if (event != LCD_EVENT_BACKLIGHT_UPDATE) |
| return NOTIFY_DONE; |
| |
| if (!pdrv) |
| return NOTIFY_DONE; |
| bdrv = aml_bl_get_driver(pdrv->index); |
| if (aml_bl_check_driver(bdrv)) |
| return NOTIFY_DONE; |
| if (bdrv->probe_done == 0) |
| return NOTIFY_DONE; |
| |
| bl_metrics_conf = &bdrv->bl_metrics_conf; |
| frame_rate = (pdrv->config.timing.sync_duration_num * 100) / |
| pdrv->config.timing.sync_duration_den; |
| frame_rate = (frame_rate + 50) / 100; |
| |
| bl_metrics_conf->frame_rate = frame_rate; |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) |
| BLPR("[%d]: %s for pwm_vs\n", bdrv->index, __func__); |
| switch (bdrv->bconf.method) { |
| case BL_CTRL_PWM: |
| if (bdrv->bconf.bl_pwm->pwm_port == BL_PWM_VS) { |
| bl_pwm = bdrv->bconf.bl_pwm; |
| if (bl_pwm) |
| bl_pwm_vs_lcd_update(bdrv, bl_pwm); |
| } |
| break; |
| case BL_CTRL_PWM_COMBO: |
| if (bdrv->bconf.bl_pwm_combo0->pwm_port == BL_PWM_VS) |
| bl_pwm = bdrv->bconf.bl_pwm_combo0; |
| else if (bdrv->bconf.bl_pwm_combo1->pwm_port == BL_PWM_VS) |
| bl_pwm = bdrv->bconf.bl_pwm_combo1; |
| if (bl_pwm) |
| bl_pwm_vs_lcd_update(bdrv, bl_pwm); |
| break; |
| #ifdef CONFIG_AMLOGIC_BL_LDIM |
| case BL_CTRL_LOCAL_DIMMING: |
| ldim_drv->vsync_change_flag = (unsigned char)(frame_rate); |
| if (ldim_drv->pwm_vs_update) |
| ldim_drv->pwm_vs_update(); |
| break; |
| #endif |
| |
| default: |
| break; |
| } |
| |
| return NOTIFY_OK; |
| } |
| |
| static struct notifier_block bl_lcd_update_nb = { |
| .notifier_call = bl_lcd_update_notifier, |
| }; |
| |
| static int bl_lcd_test_notifier(struct notifier_block *nb, |
| unsigned long event, void *data) |
| { |
| struct aml_lcd_drv_s *pdrv = (struct aml_lcd_drv_s *)data; |
| struct aml_bl_drv_s *bdrv; |
| int flag; |
| #ifdef CONFIG_AMLOGIC_BL_LDIM |
| struct aml_ldim_driver_s *ldim_drv = aml_ldim_get_driver(); |
| #endif |
| |
| /* If we aren't interested in this event, skip it immediately */ |
| if (event != LCD_EVENT_TEST_PATTERN) |
| return NOTIFY_DONE; |
| |
| if (!pdrv) |
| return NOTIFY_DONE; |
| bdrv = aml_bl_get_driver(pdrv->index); |
| if (aml_bl_check_driver(bdrv)) |
| return NOTIFY_DONE; |
| if (bdrv->probe_done == 0) |
| return NOTIFY_DONE; |
| |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) |
| BLPR("[%d]: %s for lcd test_pattern\n", bdrv->index, __func__); |
| |
| flag = (pdrv->test_state > 0) ? 1 : 0; |
| switch (bdrv->bconf.method) { |
| #ifdef CONFIG_AMLOGIC_BL_LDIM |
| case BL_CTRL_LOCAL_DIMMING: |
| if (ldim_drv->test_ctrl) |
| ldim_drv->test_ctrl(flag); |
| break; |
| #endif |
| default: |
| break; |
| } |
| |
| return NOTIFY_OK; |
| } |
| |
| static struct notifier_block bl_lcd_test_nb = { |
| .notifier_call = bl_lcd_test_notifier, |
| }; |
| |
| static int bl_gd_dimming_notifier(struct notifier_block *nb, |
| unsigned long event, void *data) |
| { |
| struct aml_bl_drv_s *bdrv = aml_bl_get_driver(0); |
| unsigned int level; |
| |
| /* If we aren't interested in this event, skip it immediately */ |
| if (event != LCD_EVENT_BACKLIGHT_GD_DIM) |
| return NOTIFY_DONE; |
| |
| if (aml_bl_check_driver(bdrv)) |
| return NOTIFY_DONE; |
| if (bdrv->probe_done == 0) |
| return NOTIFY_DONE; |
| |
| if (bdrv->brightness_bypass) |
| return NOTIFY_DONE; |
| if ((bdrv->state & BL_STATE_GD_EN) == 0) |
| return NOTIFY_DONE; |
| if (!data) |
| return NOTIFY_DONE; |
| |
| if (((bdrv->state & BL_STATE_LCD_ON) == 0) || |
| (bdrv->state & BL_STATE_BL_INIT_ON) || |
| ((bdrv->state & BL_STATE_BL_POWER_ON) == 0) || |
| ((bdrv->state & BL_STATE_BL_ON) == 0)) |
| return NOTIFY_DONE; |
| |
| /* atomic notifier, can't schedule or sleep */ |
| level = *(unsigned int *)data; |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) |
| BLPR("[%d]: %s: level_gd: %d\n", bdrv->index, __func__, level); |
| |
| bdrv->level_gd = (level < 10) ? 10 : ((level > 4095) ? 4095 : level); |
| level = bl_gd_level_map(bdrv, bdrv->level_gd); |
| aml_bl_set_level(bdrv, level); |
| |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) { |
| BLPR("[%d]: %s: %u, final level: %u, state: 0x%x\n", |
| bdrv->index, __func__, *(unsigned int *)data, |
| level, bdrv->state); |
| } |
| |
| return NOTIFY_OK; |
| } |
| |
| static struct notifier_block bl_gd_dimming_nb = { |
| .notifier_call = bl_gd_dimming_notifier, |
| }; |
| |
| static int bl_gd_sel_notifier(struct notifier_block *nb, |
| unsigned long event, void *data) |
| { |
| struct aml_bl_drv_s *bdrv = aml_bl_get_driver(0); |
| #ifdef CONFIG_AMLOGIC_BL_LDIM |
| struct aml_ldim_driver_s *ldim_drv = aml_ldim_get_driver(); |
| #endif |
| unsigned int *sel; |
| |
| /* If we aren't interested in this event, skip it immediately */ |
| if (event != LCD_EVENT_BACKLIGHT_GD_SEL) |
| return NOTIFY_DONE; |
| |
| if (aml_bl_check_driver(bdrv)) |
| return NOTIFY_DONE; |
| if (bdrv->probe_done == 0) |
| return NOTIFY_DONE; |
| |
| if (bdrv->brightness_bypass) |
| return NOTIFY_DONE; |
| if (!data) |
| return NOTIFY_DONE; |
| |
| sel = (unsigned int *)data; |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) |
| BLPR("[%d]: %s: gd_sel: %d\n", bdrv->index, __func__, *sel); |
| |
| if (*sel) { |
| #ifdef CONFIG_AMLOGIC_BL_LDIM |
| if (bdrv->bconf.method == BL_CTRL_LOCAL_DIMMING) { |
| if (ldim_drv->ld_sel_ctrl) |
| ldim_drv->ld_sel_ctrl(0); |
| } |
| #endif |
| bdrv->state |= BL_STATE_GD_EN; |
| } else { |
| bdrv->state &= ~BL_STATE_GD_EN; |
| #ifdef CONFIG_AMLOGIC_BL_LDIM |
| if (bdrv->bconf.method == BL_CTRL_LOCAL_DIMMING) { |
| if (ldim_drv->ld_sel_ctrl) |
| ldim_drv->ld_sel_ctrl(1); |
| } |
| #endif |
| } |
| |
| return NOTIFY_OK; |
| } |
| |
| static struct notifier_block bl_gd_sel_nb = { |
| .notifier_call = bl_gd_sel_notifier, |
| }; |
| |
| static void bl_notifier_init(void) |
| { |
| int ret; |
| |
| ret = aml_lcd_notifier_register(&bl_lcd_on_nb); |
| if (ret) |
| BLERR("register bl_lcd_on_nb failed\n"); |
| ret = aml_lcd_notifier_register(&bl_lcd_off_nb); |
| if (ret) |
| BLERR("register bl_lcd_off_nb failed\n"); |
| ret = aml_lcd_notifier_register(&bl_lcd_update_nb); |
| if (ret) |
| BLERR("register bl_lcd_update_nb failed\n"); |
| ret = aml_lcd_notifier_register(&bl_lcd_test_nb); |
| if (ret) |
| BLERR("register bl_lcd_test_nb failed\n"); |
| ret = aml_lcd_atomic_notifier_register(&bl_gd_dimming_nb); |
| if (ret) |
| BLERR("register bl_gd_dimming_nb failed\n"); |
| ret = aml_lcd_atomic_notifier_register(&bl_gd_sel_nb); |
| if (ret) |
| BLERR("register bl_gd_sel_nb failed\n"); |
| } |
| |
| static void bl_notifier_remove(void) |
| { |
| aml_lcd_atomic_notifier_unregister(&bl_gd_sel_nb); |
| aml_lcd_atomic_notifier_unregister(&bl_gd_dimming_nb); |
| aml_lcd_notifier_unregister(&bl_lcd_test_nb); |
| aml_lcd_notifier_unregister(&bl_lcd_update_nb); |
| aml_lcd_notifier_unregister(&bl_lcd_on_nb); |
| aml_lcd_notifier_unregister(&bl_lcd_off_nb); |
| } |
| |
| static inline void bl_vsync_handler(struct aml_bl_drv_s *bdrv) |
| { |
| struct bl_metrics_config_s *bl_metrics_conf; |
| unsigned int level = 0; |
| |
| if ((bdrv->state & BL_STATE_BL_ON) == 0) |
| return; |
| if (bdrv->brightness_bypass) |
| return; |
| |
| bl_metrics_conf = &bdrv->bl_metrics_conf; |
| if (bl_metrics_conf && bl_metrics_conf->level_buf) { |
| if (bl_metrics_conf->sum_cnt < bl_metrics_conf->frame_rate) { |
| bl_metrics_conf->level_count += bdrv->level; |
| bl_metrics_conf->brightness_count += |
| bdrv->level_brightness; |
| bl_metrics_conf->sum_cnt++; |
| } else { |
| bl_metrics_conf->level_buf[bl_metrics_conf->cnt] = |
| bl_metrics_conf->level_count / |
| bl_metrics_conf->frame_rate; |
| bl_metrics_conf->brightness_buf[bl_metrics_conf->cnt] = |
| bl_metrics_conf->brightness_count / |
| bl_metrics_conf->frame_rate; |
| bl_metrics_conf->cnt++; |
| bl_metrics_conf->sum_cnt = 0; |
| bl_metrics_conf->level_count = 0; |
| bl_metrics_conf->brightness_count = 0; |
| } |
| if (bl_metrics_conf->cnt == BL_LEVEL_CNT_MAX) { |
| bl_metrics_conf->sum_cnt = 0; |
| bl_metrics_conf->cnt = 0; |
| } |
| } |
| |
| if ((bdrv->state & BL_STATE_GD_EN) == 0) { |
| if (bdrv->bldev->props.brightness == bdrv->level) |
| return; |
| aml_bl_set_level(bdrv, bdrv->bldev->props.brightness); |
| } else { |
| level = bl_gd_level_map(bdrv, bdrv->level_gd); |
| if (level == bdrv->level) |
| return; |
| aml_bl_set_level(bdrv, level); |
| } |
| } |
| |
| static irqreturn_t bl_vsync_isr(int irq, void *data) |
| { |
| struct aml_bl_drv_s *bdrv = (struct aml_bl_drv_s *)data; |
| |
| bl_vsync_handler(bdrv); |
| return IRQ_HANDLED; |
| } |
| |
| static int bl_vsync_irq_init(struct aml_bl_drv_s *bdrv) |
| { |
| if (bdrv->res_vsync_irq[0]) { |
| if (request_irq(bdrv->res_vsync_irq[0]->start, |
| bl_vsync_isr, IRQF_SHARED, |
| "bl_vsync", (void *)bdrv)) { |
| BLPR("[%d]: can't request bl_vsync_irq\n", bdrv->index); |
| } else { |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) { |
| BLPR("[%d]: request bl_vsync_irq successful\n", |
| bdrv->index); |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| static void bl_vsync_irq_remove(struct aml_bl_drv_s *bdrv) |
| { |
| if (bdrv->res_vsync_irq[0]) |
| free_irq(bdrv->res_vsync_irq[0]->start, (void *)"bl_vsync"); |
| } |
| |
| /***************************************************************/ |
| static const char *bl_debug_usage_str = { |
| "Usage:\n" |
| " cat status ; dump backlight config\n" |
| "\n" |
| " echo freq <index> <pwm_freq> > pwm ; set pwm frequency(unit in Hz for pwm, vfreq multiple for pwm_vs)\n" |
| " echo duty <index> <pwm_duty> > pwm ; set pwm duty cycle(unit: %)\n" |
| " echo pol <index> <pwm_pol> > pwm ; set pwm polarity(unit: %)\n" |
| " echo max <index> <duty_max> > pwm ; set pwm duty_max(unit: %)\n" |
| " echo min <index> <duty_min> > pwm ; set pwm duty_min(unit: %)\n" |
| " cat pwm ; dump pwm state\n" |
| " echo free <0|1> > pwm ; set pwm_duty_free enable or disable\n" |
| "\n" |
| " echo <0|1> > power ; backlight power ctrl\n" |
| " cat power ; print backlight power state\n" |
| }; |
| |
| static ssize_t bl_debug_help(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| return sprintf(buf, "%s\n", bl_debug_usage_str); |
| } |
| |
| static ssize_t bl_status_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct aml_bl_drv_s *bdrv = dev_get_drvdata(dev); |
| struct bl_config_s *bconf; |
| struct bl_pwm_config_s *bl_pwm; |
| struct bl_pwm_config_s *pwm_combo0, *pwm_combo1; |
| #ifdef CONFIG_AMLOGIC_BL_LDIM |
| struct aml_ldim_driver_s *ldim_drv = aml_ldim_get_driver(); |
| #endif |
| #ifdef CONFIG_AMLOGIC_BL_EXTERN |
| struct bl_extern_driver_s *bext = bl_extern_get_driver(bdrv->index); |
| #endif |
| ssize_t len = 0; |
| |
| bconf = &bdrv->bconf; |
| len = sprintf(buf, "read backlight status:\n" |
| "key_valid: %d\n" |
| "config_load: %d\n" |
| "index: %d\n" |
| "name: %s\n" |
| "state: 0x%x\n" |
| "level: %d\n" |
| "level_brightness: %d\n" |
| "level_gd: %d\n" |
| "level_uboot: %d\n" |
| "level_default: %d\n" |
| "step_on_flag %d\n" |
| "brightness_bypass: %d\n\n" |
| "level_max: %d\n" |
| "level_min: %d\n" |
| "level_mid: %d\n" |
| "level_mid_mapping: %d\n\n" |
| "method: %s\n" |
| "en_gpio: %s(%d)\n" |
| "en_gpio_on: %d\n" |
| "en_gpio_off: %d\n" |
| "power_on_delay: %d\n" |
| "power_off_delay: %d\n\n", |
| bdrv->key_valid, bdrv->config_load, |
| bdrv->index, bconf->name, bdrv->state, |
| bdrv->level, bdrv->level_brightness, bdrv->level_gd, |
| bconf->level_uboot, bconf->level_default, |
| bdrv->step_on_flag, bdrv->brightness_bypass, |
| bconf->level_max, bconf->level_min, |
| bconf->level_mid, bconf->level_mid_mapping, |
| bl_method_type_to_str(bconf->method), |
| bconf->bl_gpio[bconf->en_gpio].name, |
| bconf->en_gpio, bconf->en_gpio_on, bconf->en_gpio_off, |
| bconf->power_on_delay, bconf->power_off_delay); |
| switch (bconf->method) { |
| case BL_CTRL_GPIO: |
| len += sprintf(buf + len, "to do\n"); |
| break; |
| case BL_CTRL_PWM: |
| bl_pwm = bconf->bl_pwm; |
| len += sprintf(buf + len, |
| "pwm_method: %d\n" |
| "pwm_port: %s(0x%x)\n" |
| "pwm_freq: %d\n" |
| "pwm_duty_max: %d\n" |
| "pwm_duty_min: %d\n" |
| "pwm_on_delay: %d\n" |
| "pwm_off_delay: %d\n" |
| "en_sequence_reverse: %d\n\n", |
| bl_pwm->pwm_method, |
| bl_pwm_num_to_str(bl_pwm->pwm_port), |
| bl_pwm->pwm_port, |
| bl_pwm->pwm_freq, |
| bl_pwm->pwm_duty_max, bl_pwm->pwm_duty_min, |
| bconf->pwm_on_delay, bconf->pwm_off_delay, |
| bconf->en_sequence_reverse); |
| break; |
| case BL_CTRL_PWM_COMBO: |
| pwm_combo0 = bconf->bl_pwm_combo0; |
| pwm_combo1 = bconf->bl_pwm_combo1; |
| len += sprintf(buf + len, |
| "pwm_0_level_max: %d\n" |
| "pwm_0_level_min: %d\n" |
| "pwm_0_method: %d\n" |
| "pwm_0_port: %s(0x%x)\n" |
| "pwm_0_freq: %d\n" |
| "pwm_0_duty_max: %d\n" |
| "pwm_0_duty_min: %d\n" |
| "pwm_1_level_max: %d\n" |
| "pwm_1_level_min: %d\n" |
| "pwm_1_method: %d\n" |
| "pwm_1_port: %s(0x%x)\n" |
| "pwm_1_freq: %d\n" |
| "pwm_1_duty_max: %d\n" |
| "pwm_1_duty_min: %d\n" |
| "pwm_on_delay: %d\n" |
| "pwm_off_delay: %d\n" |
| "en_sequence_reverse: %d\n\n", |
| pwm_combo0->level_max, pwm_combo0->level_min, |
| pwm_combo0->pwm_method, |
| bl_pwm_num_to_str(pwm_combo0->pwm_port), |
| pwm_combo0->pwm_port, |
| pwm_combo0->pwm_freq, |
| pwm_combo0->pwm_duty_max, |
| pwm_combo0->pwm_duty_min, |
| pwm_combo1->level_max, pwm_combo1->level_min, |
| pwm_combo1->pwm_method, |
| bl_pwm_num_to_str(pwm_combo1->pwm_port), |
| pwm_combo1->pwm_port, |
| pwm_combo1->pwm_freq, |
| pwm_combo1->pwm_duty_max, |
| pwm_combo1->pwm_duty_min, |
| bconf->pwm_on_delay, bconf->pwm_off_delay, |
| bconf->en_sequence_reverse); |
| break; |
| #ifdef CONFIG_AMLOGIC_BL_LDIM |
| case BL_CTRL_LOCAL_DIMMING: |
| if (ldim_drv->config_print) |
| ldim_drv->config_print(); |
| break; |
| #endif |
| #ifdef CONFIG_AMLOGIC_BL_EXTERN |
| case BL_CTRL_EXTERN: |
| if (bext->config_print) |
| bext->config_print(bext); |
| break; |
| #endif |
| default: |
| len += sprintf(buf + len, "wrong backlight control method\n"); |
| break; |
| } |
| return len; |
| } |
| |
| static ssize_t bl_debug_pwm_info_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct aml_bl_drv_s *bdrv = dev_get_drvdata(dev); |
| struct bl_pwm_config_s *bl_pwm; |
| struct pwm_state pstate; |
| ssize_t len = 0; |
| |
| len = sprintf(buf, "read backlight pwm info:\n"); |
| switch (bdrv->bconf.method) { |
| case BL_CTRL_PWM: |
| len += sprintf(buf + len, |
| "pwm_bypass: %d\n" |
| "pwm_duty_free: %d\n", |
| bdrv->pwm_bypass, bdrv->pwm_duty_free); |
| if (bdrv->bconf.bl_pwm) { |
| bl_pwm = bdrv->bconf.bl_pwm; |
| len += sprintf(buf + len, |
| "pwm_index: %d\n" |
| "pwm_port: %s(0x%x)\n" |
| "pwm_method: %d\n" |
| "pwm_freq: %d\n" |
| "pwm_duty_max: %d\n" |
| "pwm_duty_min: %d\n" |
| "pwm_cnt: %d\n" |
| "pwm_max: %d\n" |
| "pwm_min: %d\n" |
| "pwm_level: %d\n", |
| bl_pwm->index, |
| bl_pwm_num_to_str(bl_pwm->pwm_port), |
| bl_pwm->pwm_port, |
| bl_pwm->pwm_method, bl_pwm->pwm_freq, |
| bl_pwm->pwm_duty_max, |
| bl_pwm->pwm_duty_min, |
| bl_pwm->pwm_cnt, |
| bl_pwm->pwm_max, bl_pwm->pwm_min, |
| bl_pwm->pwm_level); |
| if (bl_pwm->pwm_duty_max > 100) { |
| len += sprintf(buf + len, |
| "pwm_duty: %d(%d%%)\n", |
| bl_pwm->pwm_duty, |
| bl_pwm->pwm_duty * 100 / 255); |
| } else { |
| len += sprintf(buf + len, |
| "pwm_duty: %d%%\n", |
| bl_pwm->pwm_duty); |
| } |
| switch (bl_pwm->pwm_port) { |
| case BL_PWM_A: |
| case BL_PWM_B: |
| case BL_PWM_C: |
| case BL_PWM_D: |
| case BL_PWM_E: |
| case BL_PWM_F: |
| case BL_PWM_AO_A: |
| case BL_PWM_AO_B: |
| case BL_PWM_AO_C: |
| case BL_PWM_AO_D: |
| case BL_PWM_AO_E: |
| case BL_PWM_AO_F: |
| case BL_PWM_AO_G: |
| case BL_PWM_AO_H: |
| if (IS_ERR_OR_NULL(bl_pwm->pwm_data.pwm)) { |
| len += sprintf(buf + len, |
| "pwm invalid\n"); |
| break; |
| } |
| pwm_get_state(bl_pwm->pwm_data.pwm, &pstate); |
| len += sprintf(buf + len, |
| "pwm state:\n" |
| " period: %lld\n" |
| " duty_cycle: %lld\n" |
| " polarity: %d\n" |
| " enabled: %d\n", |
| pstate.period, pstate.duty_cycle, |
| pstate.polarity, pstate.enabled); |
| break; |
| case BL_PWM_VS: |
| len += sprintf(buf + len, |
| "pwm_reg0: 0x%08x\n" |
| "pwm_reg1: 0x%08x\n" |
| "pwm_reg2: 0x%08x\n" |
| "pwm_reg3: 0x%08x\n", |
| lcd_vcbus_read(VPU_VPU_PWM_V0), |
| lcd_vcbus_read(VPU_VPU_PWM_V1), |
| lcd_vcbus_read(VPU_VPU_PWM_V2), |
| lcd_vcbus_read(VPU_VPU_PWM_V3)); |
| break; |
| default: |
| len += sprintf(buf + len, |
| "invalid pwm_port: 0x%x\n", |
| bl_pwm->pwm_port); |
| break; |
| } |
| } |
| break; |
| case BL_CTRL_PWM_COMBO: |
| len += sprintf(buf + len, |
| "pwm_bypass: %d\n" |
| "pwm_duty_free: %d\n", |
| bdrv->pwm_bypass, bdrv->pwm_duty_free); |
| if (bdrv->bconf.bl_pwm_combo0) { |
| bl_pwm = bdrv->bconf.bl_pwm_combo0; |
| len += sprintf(buf + len, |
| "pwm_0_index: %d\n" |
| "pwm_0_port: %s(0x%x)\n" |
| "pwm_0_method: %d\n" |
| "pwm_0_freq: %d\n" |
| "pwm_0_duty_max: %d\n" |
| "pwm_0_duty_min: %d\n" |
| "pwm_0_cnt: %d\n" |
| "pwm_0_max: %d\n" |
| "pwm_0_min: %d\n" |
| "pwm_0_level: %d\n", |
| bl_pwm->index, |
| bl_pwm_num_to_str(bl_pwm->pwm_port), |
| bl_pwm->pwm_port, |
| bl_pwm->pwm_method, bl_pwm->pwm_freq, |
| bl_pwm->pwm_duty_max, |
| bl_pwm->pwm_duty_min, |
| bl_pwm->pwm_cnt, |
| bl_pwm->pwm_max, bl_pwm->pwm_min, |
| bl_pwm->pwm_level); |
| if (bl_pwm->pwm_duty_max > 100) { |
| len += sprintf(buf + len, |
| "pwm_0_duty: %d(%d%%)\n", |
| bl_pwm->pwm_duty, |
| bl_pwm->pwm_duty * 100 / 255); |
| } else { |
| len += sprintf(buf + len, |
| "pwm_0_duty: %d%%\n", |
| bl_pwm->pwm_duty); |
| } |
| switch (bl_pwm->pwm_port) { |
| case BL_PWM_A: |
| case BL_PWM_B: |
| case BL_PWM_C: |
| case BL_PWM_D: |
| case BL_PWM_E: |
| case BL_PWM_F: |
| case BL_PWM_AO_A: |
| case BL_PWM_AO_B: |
| case BL_PWM_AO_C: |
| case BL_PWM_AO_D: |
| case BL_PWM_AO_E: |
| case BL_PWM_AO_F: |
| case BL_PWM_AO_G: |
| case BL_PWM_AO_H: |
| if (IS_ERR_OR_NULL(bl_pwm->pwm_data.pwm)) { |
| len += sprintf(buf + len, |
| "pwm invalid\n"); |
| break; |
| } |
| pwm_get_state(bl_pwm->pwm_data.pwm, &pstate); |
| len += sprintf(buf + len, |
| "pwm state:\n" |
| " period: %lld\n" |
| " duty_cycle: %lld\n" |
| " polarity: %d\n" |
| " enabled: %d\n", |
| pstate.period, pstate.duty_cycle, |
| pstate.polarity, pstate.enabled); |
| break; |
| case BL_PWM_VS: |
| len += sprintf(buf + len, |
| "pwm_0_reg0: 0x%08x\n" |
| "pwm_0_reg1: 0x%08x\n" |
| "pwm_0_reg2: 0x%08x\n" |
| "pwm_0_reg3: 0x%08x\n", |
| lcd_vcbus_read(VPU_VPU_PWM_V0), |
| lcd_vcbus_read(VPU_VPU_PWM_V1), |
| lcd_vcbus_read(VPU_VPU_PWM_V2), |
| lcd_vcbus_read(VPU_VPU_PWM_V3)); |
| break; |
| default: |
| len += sprintf(buf + len, |
| "invalid pwm_port: 0x%x\n", |
| bl_pwm->pwm_port); |
| break; |
| } |
| } |
| if (bdrv->bconf.bl_pwm_combo1) { |
| bl_pwm = bdrv->bconf.bl_pwm_combo1; |
| len += sprintf(buf + len, |
| "\n" |
| "pwm_1_index: %d\n" |
| "pwm_1_port: %s(0x%x)\n" |
| "pwm_1_method: %d\n" |
| "pwm_1_freq: %d\n" |
| "pwm_1_duty_max: %d\n" |
| "pwm_1_duty_min: %d\n" |
| "pwm_1_cnt: %d\n" |
| "pwm_1_max: %d\n" |
| "pwm_1_min: %d\n" |
| "pwm_1_level: %d\n", |
| bl_pwm->index, |
| bl_pwm_num_to_str(bl_pwm->pwm_port), |
| bl_pwm->pwm_port, |
| bl_pwm->pwm_method, bl_pwm->pwm_freq, |
| bl_pwm->pwm_duty_max, |
| bl_pwm->pwm_duty_min, |
| bl_pwm->pwm_cnt, |
| bl_pwm->pwm_max, bl_pwm->pwm_min, |
| bl_pwm->pwm_level); |
| if (bl_pwm->pwm_duty_max > 100) { |
| len += sprintf(buf + len, |
| "pwm_1_duty: %d(%d%%)\n", |
| bl_pwm->pwm_duty, |
| bl_pwm->pwm_duty * 100 / 255); |
| } else { |
| len += sprintf(buf + len, |
| "pwm_1_duty: %d%%\n", |
| bl_pwm->pwm_duty); |
| } |
| switch (bl_pwm->pwm_port) { |
| case BL_PWM_A: |
| case BL_PWM_B: |
| case BL_PWM_C: |
| case BL_PWM_D: |
| case BL_PWM_E: |
| case BL_PWM_F: |
| case BL_PWM_AO_A: |
| case BL_PWM_AO_B: |
| case BL_PWM_AO_C: |
| case BL_PWM_AO_D: |
| case BL_PWM_AO_E: |
| case BL_PWM_AO_F: |
| case BL_PWM_AO_G: |
| case BL_PWM_AO_H: |
| if (IS_ERR_OR_NULL(bl_pwm->pwm_data.pwm)) { |
| len += sprintf(buf + len, |
| "pwm invalid\n"); |
| break; |
| } |
| pwm_get_state(bl_pwm->pwm_data.pwm, &pstate); |
| len += sprintf(buf + len, |
| "pwm state:\n" |
| " period: %lld\n" |
| " duty_cycle: %lld\n" |
| " polarity: %d\n" |
| " enabled: %d\n", |
| pstate.period, pstate.duty_cycle, |
| pstate.polarity, pstate.enabled); |
| break; |
| case BL_PWM_VS: |
| len += sprintf(buf + len, |
| "pwm_1_reg0: 0x%08x\n" |
| "pwm_1_reg1: 0x%08x\n" |
| "pwm_1_reg2: 0x%08x\n" |
| "pwm_1_reg3: 0x%08x\n", |
| lcd_vcbus_read(VPU_VPU_PWM_V0), |
| lcd_vcbus_read(VPU_VPU_PWM_V1), |
| lcd_vcbus_read(VPU_VPU_PWM_V2), |
| lcd_vcbus_read(VPU_VPU_PWM_V3)); |
| break; |
| default: |
| len += sprintf(buf + len, |
| "invalid pwm_port: 0x%x\n", |
| bl_pwm->pwm_port); |
| break; |
| } |
| } |
| break; |
| default: |
| len += sprintf(buf + len, "not pwm control method\n"); |
| break; |
| } |
| return len; |
| } |
| |
| static ssize_t bl_debug_pwm_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct aml_bl_drv_s *bdrv = dev_get_drvdata(dev); |
| struct bl_pwm_config_s *bl_pwm; |
| ssize_t len = 0; |
| |
| switch (bdrv->bconf.method) { |
| case BL_CTRL_PWM: |
| if (bdrv->bconf.bl_pwm) { |
| bl_pwm = bdrv->bconf.bl_pwm; |
| len += sprintf(buf + len, |
| "pwm: freq=%d, pol=%d, duty_max=%d, duty_min=%d, ", |
| bl_pwm->pwm_freq, bl_pwm->pwm_method, |
| bl_pwm->pwm_duty_max, |
| bl_pwm->pwm_duty_min); |
| if (bl_pwm->pwm_duty_max > 100) { |
| len += sprintf(buf + len, |
| "duty_value=%d(%d%%)\n", |
| bl_pwm->pwm_duty, |
| bl_pwm->pwm_duty * 100 / 255); |
| } else { |
| len += sprintf(buf + len, |
| "duty_value=%d%%\n", |
| bl_pwm->pwm_duty); |
| } |
| } |
| break; |
| case BL_CTRL_PWM_COMBO: |
| if (bdrv->bconf.bl_pwm_combo0) { |
| bl_pwm = bdrv->bconf.bl_pwm_combo0; |
| len += sprintf(buf + len, |
| "pwm_0: freq=%d, pol=%d, duty_max=%d, duty_min=%d, ", |
| bl_pwm->pwm_freq, bl_pwm->pwm_method, |
| bl_pwm->pwm_duty_max, |
| bl_pwm->pwm_duty_min); |
| if (bl_pwm->pwm_duty_max > 100) { |
| len += sprintf(buf + len, |
| "duty_value=%d(%d%%)\n", |
| bl_pwm->pwm_duty, |
| bl_pwm->pwm_duty * 100 / 255); |
| } else { |
| len += sprintf(buf + len, |
| "duty_value=%d%%\n", |
| bl_pwm->pwm_duty); |
| } |
| } |
| |
| if (bdrv->bconf.bl_pwm_combo1) { |
| bl_pwm = bdrv->bconf.bl_pwm_combo1; |
| len += sprintf(buf + len, |
| "pwm_1: freq=%d, pol=%d, duty_max=%d, duty_min=%d, ", |
| bl_pwm->pwm_freq, bl_pwm->pwm_method, |
| bl_pwm->pwm_duty_max, |
| bl_pwm->pwm_duty_min); |
| if (bl_pwm->pwm_duty_max > 100) { |
| len += sprintf(buf + len, |
| "duty_value=%d(%d%%)\n", |
| bl_pwm->pwm_duty, |
| bl_pwm->pwm_duty * 100 / 255); |
| } else { |
| len += sprintf(buf + len, |
| "duty_value=%d%%\n", |
| bl_pwm->pwm_duty); |
| } |
| } |
| break; |
| default: |
| break; |
| } |
| return len; |
| } |
| |
| #define BL_DEBUG_PWM_FREQ 0 |
| #define BL_DEBUG_PWM_DUTY 1 |
| #define BL_DEBUG_PWM_POL 2 |
| #define BL_DEBUG_PWM_DUTY_MAX 3 |
| #define BL_DEBUG_PWM_DUTY_MIN 4 |
| static void bl_debug_pwm_set(struct aml_bl_drv_s *bdrv, unsigned int index, |
| unsigned int value, int state) |
| { |
| struct bl_config_s *bconf = &bdrv->bconf; |
| struct bl_pwm_config_s *bl_pwm = NULL; |
| unsigned long long temp; |
| unsigned int pwm_range, temp_duty; |
| |
| if (aml_bl_check_driver(bdrv)) |
| return; |
| |
| mutex_lock(&bl_level_mutex); |
| |
| switch (bconf->method) { |
| case BL_CTRL_PWM: |
| bl_pwm = bconf->bl_pwm; |
| break; |
| case BL_CTRL_PWM_COMBO: |
| if (index == 0) |
| bl_pwm = bconf->bl_pwm_combo0; |
| else |
| bl_pwm = bconf->bl_pwm_combo1; |
| break; |
| default: |
| BLERR("not pwm control method\n"); |
| break; |
| } |
| if (bl_pwm) { |
| switch (state) { |
| case BL_DEBUG_PWM_FREQ: |
| bl_pwm->pwm_freq = value; |
| if (bl_pwm->pwm_freq < 50) |
| bl_pwm->pwm_freq = 50; |
| bl_pwm_config_init(bl_pwm); |
| bl_pwm_set_duty(bdrv, bl_pwm); |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) { |
| BLPR("[%d]: set index(%d) pwm_port(0x%x) freq: %dHz\n", |
| bdrv->index, index, |
| bl_pwm->pwm_port, bl_pwm->pwm_freq); |
| } |
| break; |
| case BL_DEBUG_PWM_DUTY: |
| bl_pwm->pwm_duty = value; |
| bl_pwm_set_duty(bdrv, bl_pwm); |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) { |
| if (bl_pwm->pwm_duty_max > 100) { |
| BLPR("[%d]: set index(%d) pwm_port(0x%x) duty: %d\n", |
| bdrv->index, index, |
| bl_pwm->pwm_port, |
| bl_pwm->pwm_duty); |
| } else { |
| BLPR("[%d]: set index(%d) pwm_port(0x%x) duty: %d%%\n", |
| bdrv->index, index, |
| bl_pwm->pwm_port, |
| bl_pwm->pwm_duty); |
| } |
| } |
| break; |
| case BL_DEBUG_PWM_POL: |
| bl_pwm->pwm_method = value; |
| bl_pwm_config_init(bl_pwm); |
| bl_pwm_set_duty(bdrv, bl_pwm); |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) { |
| BLPR("[%d]: set index(%d) pwm_port(0x%x) method: %d\n", |
| bdrv->index, index, bl_pwm->pwm_port, |
| bl_pwm->pwm_method); |
| } |
| break; |
| case BL_DEBUG_PWM_DUTY_MAX: |
| bl_pwm->pwm_duty_max = value; |
| if (bl_pwm->pwm_duty_max > 100) |
| pwm_range = 2550; |
| else |
| pwm_range = 1000; |
| temp = bl_pwm->pwm_level; |
| temp_duty = bl_do_div((temp * pwm_range), |
| bl_pwm->pwm_cnt); |
| bl_pwm->pwm_duty = (temp_duty + 5) / 10; |
| temp = bl_pwm->pwm_min; |
| temp_duty = bl_do_div((temp * pwm_range), |
| bl_pwm->pwm_cnt); |
| bl_pwm->pwm_duty_min = (temp_duty + 5) / 10; |
| bl_pwm_config_init(bl_pwm); |
| bl_pwm_set_duty(bdrv, bl_pwm); |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) { |
| if (bl_pwm->pwm_duty_max > 100) { |
| BLPR("[%d]: set index(%d) pwm_port(0x%x) duty_max: %d\n", |
| bdrv->index, index, |
| bl_pwm->pwm_port, |
| bl_pwm->pwm_duty_max); |
| } else { |
| BLPR("[%d]: set index(%d) pwm_port(0x%x) duty_max: %d%%\n", |
| bdrv->index, index, |
| bl_pwm->pwm_port, |
| bl_pwm->pwm_duty_max); |
| } |
| } |
| break; |
| case BL_DEBUG_PWM_DUTY_MIN: |
| bl_pwm->pwm_duty_min = value; |
| bl_pwm_config_init(bl_pwm); |
| bl_pwm_set_duty(bdrv, bl_pwm); |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) { |
| if (bl_pwm->pwm_duty_max > 100) { |
| BLPR("[%d]: set index(%d) pwm_port(0x%x) duty_min: %d\n", |
| bdrv->index, index, |
| bl_pwm->pwm_port, |
| bl_pwm->pwm_duty_min); |
| } else { |
| BLPR("[%d]: set index(%d) pwm_port(0x%x) duty_min: %d%%\n", |
| bdrv->index, index, |
| bl_pwm->pwm_port, |
| bl_pwm->pwm_duty_min); |
| } |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| mutex_unlock(&bl_level_mutex); |
| } |
| |
| static ssize_t bl_debug_pwm_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| struct aml_bl_drv_s *bdrv = dev_get_drvdata(dev); |
| unsigned int ret; |
| unsigned int index = 0, val = 0; |
| |
| switch (buf[0]) { |
| case 'f': |
| if (buf[3] == 'q') { /* frequency */ |
| ret = sscanf(buf, "freq %d %d", &index, &val); |
| if (ret == 2) { |
| bl_debug_pwm_set(bdrv, index, val, |
| BL_DEBUG_PWM_FREQ); |
| } else { |
| BLERR("invalid parameters\n"); |
| } |
| } else if (buf[3] == 'e') { /* duty free */ |
| ret = sscanf(buf, "free %d", &val); |
| if (ret == 1) { |
| bdrv->pwm_duty_free = (unsigned char)val; |
| /*BLPR("[d]: set pwm_duty_free: %d\n", |
| * bdrv->index, bdrv->pwm_duty_free); |
| */ |
| } else { |
| BLERR("invalid parameters\n"); |
| } |
| } |
| break; |
| case 'd': /* duty */ |
| ret = sscanf(buf, "duty %d %d", &index, &val); |
| if (ret == 2) |
| bl_debug_pwm_set(bdrv, index, val, BL_DEBUG_PWM_DUTY); |
| else |
| BLERR("invalid parameters\n"); |
| break; |
| case 'p': /* polarity */ |
| ret = sscanf(buf, "pol %d %d", &index, &val); |
| if (ret == 2) |
| bl_debug_pwm_set(bdrv, index, val, BL_DEBUG_PWM_POL); |
| else |
| BLERR("invalid parameters\n"); |
| break; |
| case 'b': /* bypass */ |
| ret = sscanf(buf, "bypass %d", &val); |
| if (ret == 1) { |
| bdrv->pwm_bypass = (unsigned char)val; |
| BLPR("[%d]: set pwm_bypass: %d\n", |
| bdrv->index, bdrv->pwm_bypass); |
| } else { |
| BLERR("invalid parameters\n"); |
| } |
| break; |
| case 'm': |
| if (buf[1] == 'a') { /* max */ |
| ret = sscanf(buf, "max %d %d", &index, &val); |
| if (ret == 2) { |
| bl_debug_pwm_set(bdrv, index, val, |
| BL_DEBUG_PWM_DUTY_MAX); |
| } else { |
| BLERR("invalid parameters\n"); |
| } |
| } else if (buf[1] == 'i') { /* min */ |
| ret = sscanf(buf, "min %d %d", &index, &val); |
| if (ret == 2) { |
| bl_debug_pwm_set(bdrv, index, val, |
| BL_DEBUG_PWM_DUTY_MIN); |
| } else { |
| BLERR("invalid parameters\n"); |
| } |
| } |
| break; |
| default: |
| BLERR("wrong command\n"); |
| break; |
| } |
| |
| return count; |
| } |
| |
| static ssize_t bl_debug_power_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct aml_bl_drv_s *bdrv = dev_get_drvdata(dev); |
| int pwr_state, real_state; |
| |
| if (bdrv->state & BL_STATE_BL_POWER_ON) |
| pwr_state = 1; |
| else |
| pwr_state = 0; |
| if (bdrv->state & BL_STATE_BL_ON) |
| real_state = 1; |
| else |
| real_state = 0; |
| return sprintf(buf, "backlight power state: %d, real state: %d\n", |
| pwr_state, real_state); |
| } |
| |
| static ssize_t bl_debug_power_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| struct aml_bl_drv_s *bdrv = dev_get_drvdata(dev); |
| unsigned int ret; |
| unsigned int temp = 0; |
| |
| ret = kstrtouint(buf, 10, &temp); |
| if (ret != 0) { |
| BLERR("invalid data\n"); |
| return -EINVAL; |
| } |
| |
| BLPR("[%d]: power control: %u\n", bdrv->index, temp); |
| if (temp == 0) { |
| bdrv->state &= ~BL_STATE_BL_POWER_ON; |
| if (bdrv->state & BL_STATE_BL_ON) |
| bl_power_off(bdrv); |
| } else { |
| bdrv->state |= BL_STATE_BL_POWER_ON; |
| if ((bdrv->state & BL_STATE_BL_ON) == 0) |
| bl_power_on(bdrv); |
| } |
| |
| return count; |
| } |
| |
| static ssize_t bl_debug_step_on_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct aml_bl_drv_s *bdrv = dev_get_drvdata(dev); |
| |
| return sprintf(buf, "backlight step_on: %d\n", bdrv->step_on_flag); |
| } |
| |
| static ssize_t bl_debug_step_on_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| struct aml_bl_drv_s *bdrv = dev_get_drvdata(dev); |
| unsigned int ret; |
| unsigned int temp = 0; |
| |
| ret = kstrtouint(buf, 10, &temp); |
| if (ret != 0) { |
| BLERR("invalid data\n"); |
| return -EINVAL; |
| } |
| |
| bdrv->step_on_flag = (unsigned char)temp; |
| BLPR("[%d]: step_on: %u\n", bdrv->index, bdrv->step_on_flag); |
| |
| return count; |
| } |
| |
| static ssize_t bl_debug_delay_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct aml_bl_drv_s *bdrv = dev_get_drvdata(dev); |
| |
| return sprintf(buf, "bl power delay: on=%dms, off=%dms\n", |
| bdrv->bconf.power_on_delay, |
| bdrv->bconf.power_off_delay); |
| } |
| |
| static ssize_t bl_debug_delay_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| struct aml_bl_drv_s *bdrv = dev_get_drvdata(dev); |
| unsigned int ret; |
| unsigned int val[2]; |
| |
| ret = sscanf(buf, "%d %d", &val[0], &val[1]); |
| if (ret == 2) { |
| bdrv->bconf.power_on_delay = val[0]; |
| bdrv->bconf.power_off_delay = val[1]; |
| pr_info("[%d]: set bl power_delay: on=%dms, off=%dms\n", |
| bdrv->index, val[0], val[1]); |
| } else { |
| pr_info("invalid data\n"); |
| return -EINVAL; |
| } |
| |
| return count; |
| } |
| |
| static ssize_t bl_debug_brightness_bypass_show(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| struct aml_bl_drv_s *bdrv = dev_get_drvdata(dev); |
| |
| return sprintf(buf, "%d\n", bdrv->brightness_bypass); |
| } |
| |
| static ssize_t bl_debug_brightness_bypass_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| struct aml_bl_drv_s *bdrv = dev_get_drvdata(dev); |
| unsigned int temp; |
| int ret; |
| |
| ret = kstrtouint(buf, 10, &temp); |
| if (ret != 0) { |
| BLERR("invalid data\n"); |
| return -EINVAL; |
| } |
| bdrv->brightness_bypass = (unsigned char)temp; |
| return count; |
| } |
| |
| static void bl_brightness_metrics_calc(struct aml_bl_drv_s *bdrv) |
| { |
| struct bl_metrics_config_s *bl_metrics_conf = &bdrv->bl_metrics_conf; |
| unsigned int j = BL_LEVEL_CNT_MAX; |
| unsigned int i = 0; |
| unsigned int level_sum = 0; |
| unsigned int brightness_sum = 0; |
| unsigned int cnt = 0; |
| unsigned int temp; |
| |
| cnt = bl_metrics_conf->cnt; |
| temp = bl_metrics_conf->times; |
| memcpy(&bl_metrics_conf->level_buf[j], |
| bl_metrics_conf->level_buf, |
| (sizeof(unsigned int) * BL_LEVEL_CNT_MAX)); |
| |
| memcpy(&bl_metrics_conf->brightness_buf[j], |
| bl_metrics_conf->brightness_buf, |
| (sizeof(unsigned int) * BL_LEVEL_CNT_MAX)); |
| |
| for (i = cnt + j; i > (cnt + j - temp); i--) { |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_ADV) { |
| BLPR("cnt: %d, %d: brightness_buf: %d, level_buf: %d\n", |
| cnt, i, |
| bl_metrics_conf->brightness_buf[i], |
| bl_metrics_conf->level_buf[i]); |
| } |
| level_sum += bl_metrics_conf->level_buf[i]; |
| brightness_sum += bl_metrics_conf->brightness_buf[i]; |
| } |
| |
| bl_metrics_conf->level_metrics = level_sum / temp; |
| bl_metrics_conf->brightness_metrics = brightness_sum / temp; |
| } |
| |
| static ssize_t bl_brightness_metrics_show(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| struct aml_bl_drv_s *bdrv = dev_get_drvdata(dev); |
| struct bl_metrics_config_s *bl_metrics_conf = &bdrv->bl_metrics_conf; |
| |
| if (!bl_metrics_conf->level_buf) |
| return sprintf(buf, "bl_metrics_conf have no level_buf\n"); |
| |
| bl_brightness_metrics_calc(bdrv); |
| |
| return sprintf(buf, "brightness_metrics: %d, level_metrics: %d\n", |
| bl_metrics_conf->brightness_metrics, |
| bl_metrics_conf->level_metrics); |
| } |
| |
| static ssize_t bl_brightness_metrics_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| struct aml_bl_drv_s *bdrv = dev_get_drvdata(dev); |
| struct bl_metrics_config_s *bl_metrics_conf = &bdrv->bl_metrics_conf; |
| unsigned int temp; |
| int ret; |
| |
| ret = kstrtouint(buf, 10, &temp); |
| if (ret != 0) { |
| BLERR("invalid data\n"); |
| return -EINVAL; |
| } |
| |
| if (!bl_metrics_conf->level_buf) { |
| BLERR("get no brightness value\n"); |
| return -EINVAL; |
| } |
| |
| if (temp > (BL_LEVEL_CNT_MAX / 60)) { |
| BLPR("max support 60min\n"); |
| bl_metrics_conf->times = BL_LEVEL_CNT_MAX; |
| } else { |
| bl_metrics_conf->times = temp * 60; |
| } |
| |
| bl_brightness_metrics_calc(bdrv); |
| |
| BLPR("time: %d, brightness_metrics: %d, level_metrics: %d\n", |
| bl_metrics_conf->times, |
| bl_metrics_conf->brightness_metrics, |
| bl_metrics_conf->level_metrics); |
| |
| return count; |
| } |
| |
| static ssize_t bl_debug_level_show(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| struct aml_bl_drv_s *bdrv = dev_get_drvdata(dev); |
| |
| return sprintf(buf, "%d\n", bdrv->level); |
| } |
| |
| static ssize_t bl_debug_level_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| struct aml_bl_drv_s *bdrv = dev_get_drvdata(dev); |
| unsigned int level = 0; |
| unsigned int ret; |
| |
| ret = kstrtouint(buf, 10, &level); |
| if (ret != 0) { |
| BLERR("invalid data\n"); |
| return -EINVAL; |
| } |
| |
| if (!bdrv->brightness_bypass) |
| bdrv->brightness_bypass = 1; |
| |
| mutex_lock(&bl_level_mutex); |
| |
| aml_bl_set_level(bdrv, level); |
| BLPR("[%d]: %s: %u, state: 0x%x\n", |
| bdrv->index, __func__, level, bdrv->state); |
| |
| mutex_unlock(&bl_level_mutex); |
| |
| return count; |
| } |
| |
| static ssize_t bl_debug_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| struct aml_bl_drv_s *bdrv = dev_get_drvdata(dev); |
| unsigned int val, temp, ret; |
| |
| switch (buf[0]) { |
| case 'g': /* gd */ |
| ret = sscanf(buf, "gd %d", &val); |
| if (ret == 1) { |
| if ((bdrv->state & BL_STATE_GD_EN) == 0) { |
| temp = 1; |
| aml_lcd_atomic_notifier_call_chain(LCD_EVENT_BACKLIGHT_GD_SEL, |
| &temp); |
| } |
| mutex_lock(&bl_level_mutex); |
| aml_lcd_atomic_notifier_call_chain(LCD_EVENT_BACKLIGHT_GD_DIM, &val); |
| mutex_unlock(&bl_level_mutex); |
| } else { |
| BLERR("invalid parameters\n"); |
| } |
| break; |
| case 'b': /* brightness */ |
| ret = sscanf(buf, "brightness %d", &val); |
| if (ret == 1) { |
| if (!bdrv->brightness_bypass) |
| bdrv->brightness_bypass = 1; |
| |
| mutex_lock(&bl_status_mutex); |
| bdrv->level_brightness = bl_brightness_level_map(bdrv, val); |
| |
| if (bdrv->level_brightness == 0) { |
| if (bdrv->state & BL_STATE_BL_ON) |
| bl_power_off(bdrv); |
| } else { |
| if ((bdrv->state & BL_STATE_GD_EN) == 0) { |
| aml_bl_set_level(bdrv, bdrv->level_brightness); |
| BLPR("[%d]: debug brightness: %u->%u, state: 0x%x\n", |
| bdrv->index, val, bdrv->level_brightness, |
| bdrv->state); |
| } else { |
| temp = bl_gd_level_map(bdrv, bdrv->level_gd); |
| aml_bl_set_level(bdrv, temp); |
| BLPR("[%d]: debug brightness: %u->%u, state: 0x%x\n", |
| bdrv->index, val, temp, bdrv->state); |
| } |
| |
| if ((bdrv->state & BL_STATE_BL_ON) == 0) |
| bl_power_on(bdrv); |
| } |
| mutex_unlock(&bl_status_mutex); |
| } else { |
| BLERR("invalid parameters\n"); |
| } |
| break; |
| case 'l': /* level */ |
| ret = sscanf(buf, "level %d", &val); |
| if (ret == 1) { |
| if (!bdrv->brightness_bypass) |
| bdrv->brightness_bypass = 1; |
| |
| mutex_lock(&bl_level_mutex); |
| |
| temp = bl_brightness_level_map(bdrv, val); |
| aml_bl_set_level(bdrv, temp); |
| BLPR("[%d]: debug level: %u, state: 0x%x\n", |
| bdrv->index, temp, bdrv->state); |
| |
| mutex_unlock(&bl_level_mutex); |
| } else { |
| BLERR("invalid parameters\n"); |
| } |
| break; |
| case 'o': /* off */ |
| if (bdrv->state & BL_STATE_GD_EN) { |
| temp = 0; |
| aml_lcd_atomic_notifier_call_chain(LCD_EVENT_BACKLIGHT_GD_SEL, &temp); |
| } |
| bdrv->brightness_bypass = 0; |
| break; |
| default: |
| BLERR("wrong command\n"); |
| break; |
| } |
| |
| return count; |
| } |
| |
| static struct device_attribute bl_debug_attrs[] = { |
| __ATTR(help, 0444, bl_debug_help, NULL), |
| __ATTR(status, 0444, bl_status_show, NULL), |
| __ATTR(pwm_info, 0444, bl_debug_pwm_info_show, NULL), |
| __ATTR(pwm, 0644, bl_debug_pwm_show, bl_debug_pwm_store), |
| __ATTR(power_on, 0644, bl_debug_power_show, bl_debug_power_store), |
| __ATTR(step_on, 0644, bl_debug_step_on_show, bl_debug_step_on_store), |
| __ATTR(delay, 0644, bl_debug_delay_show, bl_debug_delay_store), |
| __ATTR(brightness_bypass, 0644, bl_debug_brightness_bypass_show, |
| bl_debug_brightness_bypass_store), |
| __ATTR(brightness_metrics, 0644, bl_brightness_metrics_show, |
| bl_brightness_metrics_store), |
| __ATTR(debug_level, 0644, bl_debug_level_show, bl_debug_level_store), |
| __ATTR(debug, 0644, bl_debug_help, bl_debug_store), |
| }; |
| |
| static int bl_debug_file_creat(struct aml_bl_drv_s *bdrv) |
| { |
| int i; |
| |
| for (i = 0; i < ARRAY_SIZE(bl_debug_attrs); i++) { |
| if (device_create_file(bdrv->dev, &bl_debug_attrs[i])) { |
| BLERR("[%d]: create debug attribute %s fail\n", |
| bdrv->index, bl_debug_attrs[i].attr.name); |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int bl_debug_file_remove(struct aml_bl_drv_s *bdrv) |
| { |
| int i; |
| |
| for (i = 0; i < ARRAY_SIZE(bl_debug_attrs); i++) |
| device_remove_file(bdrv->dev, &bl_debug_attrs[i]); |
| |
| return 0; |
| } |
| |
| /* ************************************************************* */ |
| static int bl_io_open(struct inode *inode, struct file *file) |
| { |
| struct aml_bl_drv_s *bdrv; |
| |
| bdrv = container_of(inode->i_cdev, struct aml_bl_drv_s, cdev); |
| file->private_data = bdrv; |
| |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) |
| BLPR("%s\n", __func__); |
| |
| return 0; |
| } |
| |
| static int bl_io_release(struct inode *inode, struct file *file) |
| { |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) |
| BLPR("%s\n", __func__); |
| file->private_data = NULL; |
| return 0; |
| } |
| |
| static long bl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
| { |
| return 0; |
| } |
| |
| #ifdef CONFIG_COMPAT |
| static long bl_compat_ioctl(struct file *file, unsigned int cmd, |
| unsigned long arg) |
| { |
| unsigned long ret; |
| |
| arg = (unsigned long)compat_ptr(arg); |
| ret = bl_ioctl(file, cmd, arg); |
| return ret; |
| } |
| #endif |
| |
| static const struct file_operations bl_fops = { |
| .owner = THIS_MODULE, |
| .open = bl_io_open, |
| .release = bl_io_release, |
| .unlocked_ioctl = bl_ioctl, |
| #ifdef CONFIG_COMPAT |
| .compat_ioctl = bl_compat_ioctl, |
| #endif |
| }; |
| |
| static int bl_cdev_add(struct aml_bl_drv_s *bdrv, struct device *parent) |
| { |
| dev_t devno; |
| int ret = 0; |
| |
| if (!bdrv) { |
| BLERR("%s: bdrv is null\n", __func__); |
| return -1; |
| } |
| if (!bl_cdev) { |
| ret = 1; |
| goto bl_cdev_add_failed; |
| } |
| |
| devno = MKDEV(MAJOR(bl_cdev->devno), bdrv->index); |
| |
| cdev_init(&bdrv->cdev, &bl_fops); |
| bdrv->cdev.owner = THIS_MODULE; |
| ret = cdev_add(&bdrv->cdev, devno, 1); |
| if (ret) { |
| ret = 2; |
| goto bl_cdev_add_failed; |
| } |
| |
| bdrv->dev = device_create(bl_cdev->class, parent, |
| devno, NULL, "bl%d", bdrv->index); |
| if (IS_ERR_OR_NULL(bdrv->dev)) { |
| ret = 3; |
| goto bl_cdev_add_failed1; |
| } |
| |
| dev_set_drvdata(bdrv->dev, bdrv); |
| bdrv->dev->of_node = parent->of_node; |
| |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) |
| BLPR("[%d]: %s OK\n", bdrv->index, __func__); |
| return 0; |
| |
| bl_cdev_add_failed1: |
| cdev_del(&bdrv->cdev); |
| bl_cdev_add_failed: |
| BLERR("[%d]: %s: failed: %d\n", bdrv->index, __func__, ret); |
| return -1; |
| } |
| |
| static void bl_cdev_remove(struct aml_bl_drv_s *bdrv) |
| { |
| dev_t devno; |
| |
| if (!bl_cdev || !bdrv) |
| return; |
| |
| devno = MKDEV(MAJOR(bl_cdev->devno), bdrv->index); |
| device_destroy(bl_cdev->class, devno); |
| cdev_del(&bdrv->cdev); |
| } |
| |
| static int bl_global_init_once(void) |
| { |
| int ret; |
| |
| if (bl_global_init_flag) { |
| bl_global_init_flag++; |
| return 0; |
| } |
| bl_global_init_flag++; |
| |
| bl_notifier_init(); |
| |
| bl_cdev = kzalloc(sizeof(*bl_cdev), GFP_KERNEL); |
| if (!bl_cdev) |
| return -1; |
| |
| ret = alloc_chrdev_region(&bl_cdev->devno, 0, |
| LCD_MAX_DRV, BL_CDEV_NAME); |
| if (ret) { |
| ret = 1; |
| goto bl_global_init_once_err; |
| } |
| |
| bl_cdev->class = class_create(THIS_MODULE, "aml_bl"); |
| if (IS_ERR_OR_NULL(bl_cdev->class)) { |
| ret = 2; |
| goto bl_global_init_once_err_1; |
| } |
| |
| return 0; |
| |
| bl_global_init_once_err_1: |
| unregister_chrdev_region(bl_cdev->devno, LCD_MAX_DRV); |
| bl_global_init_once_err: |
| kfree(bl_cdev); |
| bl_cdev = NULL; |
| BLERR("%s: failed: %d\n", __func__, ret); |
| return -1; |
| } |
| |
| static void bl_global_remove_once(void) |
| { |
| if (bl_global_init_flag > 1) { |
| bl_global_init_flag--; |
| return; |
| } |
| bl_global_init_flag--; |
| |
| bl_notifier_remove(); |
| |
| if (!bl_cdev) |
| return; |
| |
| class_destroy(bl_cdev->class); |
| unregister_chrdev_region(bl_cdev->devno, LCD_MAX_DRV); |
| kfree(bl_cdev); |
| bl_cdev = NULL; |
| } |
| |
| /* **************************************** */ |
| |
| #ifdef CONFIG_PM |
| static int aml_bl_suspend(struct platform_device *pdev, pm_message_t state) |
| { |
| return 0; |
| } |
| |
| static int aml_bl_resume(struct platform_device *pdev) |
| { |
| return 0; |
| } |
| #endif |
| |
| #ifdef CONFIG_OF |
| static struct bl_data_s bl_data_g12a = { |
| .chip_type = LCD_CHIP_G12A, |
| .chip_name = "g12a", |
| .pwm_vs_flag = 0, |
| }; |
| |
| static struct bl_data_s bl_data_g12b = { |
| .chip_type = LCD_CHIP_G12B, |
| .chip_name = "g12b", |
| .pwm_vs_flag = 0, |
| }; |
| |
| #ifndef CONFIG_AMLOGIC_REMOVE_OLD |
| static struct bl_data_s bl_data_tl1 = { |
| .chip_type = LCD_CHIP_TL1, |
| .chip_name = "tl1", |
| .pwm_vs_flag = 1, |
| }; |
| #endif |
| |
| static struct bl_data_s bl_data_sm1 = { |
| .chip_type = LCD_CHIP_SM1, |
| .chip_name = "sm1", |
| .pwm_vs_flag = 0, |
| }; |
| |
| static struct bl_data_s bl_data_tm2 = { |
| .chip_type = LCD_CHIP_TM2, |
| .chip_name = "tm2", |
| .pwm_vs_flag = 1, |
| }; |
| |
| static struct bl_data_s bl_data_tm2b = { |
| .chip_type = LCD_CHIP_TM2B, |
| .chip_name = "tm2b", |
| .pwm_vs_flag = 1, |
| }; |
| |
| static struct bl_data_s bl_data_t5 = { |
| .chip_type = LCD_CHIP_T5, |
| .chip_name = "t5", |
| .pwm_vs_flag = 1, |
| }; |
| |
| static struct bl_data_s bl_data_t5d = { |
| .chip_type = LCD_CHIP_T5D, |
| .chip_name = "t5d", |
| .pwm_vs_flag = 1, |
| }; |
| |
| static struct bl_data_s bl_data_t7 = { |
| .chip_type = LCD_CHIP_T7, |
| .chip_name = "t7", |
| .pwm_vs_flag = 1, |
| }; |
| |
| static struct bl_data_s bl_data_t3 = { |
| .chip_type = LCD_CHIP_T3, |
| .chip_name = "t3", |
| .pwm_vs_flag = 1, |
| }; |
| |
| static struct bl_data_s bl_data_t5w = { |
| .chip_type = LCD_CHIP_T5W, |
| .chip_name = "t5w", |
| .pwm_vs_flag = 1, |
| }; |
| |
| static const struct of_device_id bl_dt_match_table[] = { |
| { |
| .compatible = "amlogic, backlight-g12a", |
| .data = &bl_data_g12a, |
| }, |
| { |
| .compatible = "amlogic, backlight-g12b", |
| .data = &bl_data_g12b, |
| }, |
| #ifndef CONFIG_AMLOGIC_REMOVE_OLD |
| { |
| .compatible = "amlogic, backlight-tl1", |
| .data = &bl_data_tl1, |
| }, |
| #endif |
| { |
| .compatible = "amlogic, backlight-sm1", |
| .data = &bl_data_sm1, |
| }, |
| { |
| .compatible = "amlogic, backlight-tm2", |
| .data = &bl_data_tm2, |
| }, |
| { |
| .compatible = "amlogic, backlight-tm2b", |
| .data = &bl_data_tm2b, |
| }, |
| { |
| .compatible = "amlogic, backlight-t5", |
| .data = &bl_data_t5, |
| }, |
| { |
| .compatible = "amlogic, backlight-t5d", |
| .data = &bl_data_t5d, |
| }, |
| { |
| .compatible = "amlogic, backlight-t7", |
| .data = &bl_data_t7, |
| }, |
| { |
| .compatible = "amlogic, backlight-t3", |
| .data = &bl_data_t3, |
| }, |
| { |
| .compatible = "amlogic, backlight-t5w", |
| .data = &bl_data_t5w, |
| }, |
| {} |
| }; |
| #endif |
| |
| static void aml_bl_power_init(struct aml_bl_drv_s *bdrv) |
| { |
| struct bl_config_s *bconf = &bdrv->bconf; |
| |
| /* update bl status */ |
| bdrv->state = (BL_STATE_LCD_ON | BL_STATE_BL_POWER_ON); |
| bdrv->on_request = 1; |
| /* lcd power on sequence control */ |
| if (bconf->method < BL_CTRL_MAX) { |
| lcd_queue_delayed_work(&bdrv->delayed_on_work, |
| bconf->power_on_delay); |
| } else { |
| BLERR("[%d]: wrong backlight control method\n", bdrv->index); |
| } |
| } |
| |
| static void bl_init_status_update(struct aml_bl_drv_s *bdrv) |
| { |
| struct aml_lcd_drv_s *pdrv; |
| |
| pdrv = aml_lcd_get_driver(bdrv->index); |
| if (!pdrv) |
| return; |
| |
| /* default disable lcd & backlight */ |
| if ((pdrv->status & LCD_STATUS_IF_ON) == 0) |
| return; |
| |
| if (pdrv->boot_ctrl) { |
| if (pdrv->boot_ctrl->init_level == LCD_INIT_LEVEL_KERNEL_ON) { |
| BLPR("[%d]: power on for init_level %d\n", |
| bdrv->index, pdrv->boot_ctrl->init_level); |
| aml_bl_power_init(bdrv); |
| return; |
| } |
| } |
| |
| /* update bl status */ |
| bdrv->state = (BL_STATE_LCD_ON | BL_STATE_BL_POWER_ON | BL_STATE_BL_ON); |
| bdrv->on_request = 1; |
| |
| mutex_lock(&bl_level_mutex); |
| if (bdrv->brightness_bypass) { |
| aml_bl_set_level(bdrv, bdrv->level_init_on); |
| } else { |
| bdrv->level_brightness = bl_brightness_level_map(bdrv, |
| bdrv->bldev->props.brightness); |
| aml_bl_init_level(bdrv, bdrv->level_brightness); |
| } |
| mutex_unlock(&bl_level_mutex); |
| |
| switch (bdrv->bconf.method) { |
| case BL_CTRL_PWM: |
| case BL_CTRL_PWM_COMBO: |
| bl_pwm_pinmux_set(bdrv, 1); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static void aml_bl_config_probe_work(struct work_struct *work) |
| { |
| struct aml_bl_drv_s *bdrv; |
| struct bl_metrics_config_s *bl_metrics_conf = NULL; |
| struct backlight_properties props; |
| struct backlight_device *bldev; |
| char bl_name[10]; |
| int index; |
| int ret; |
| |
| bdrv = container_of(work, struct aml_bl_drv_s, config_probe_work); |
| |
| index = bdrv->index; |
| bdrv->pinmux_flag = 0xff; |
| bdrv->bconf.level_default = 128; |
| bdrv->bconf.level_mid = 128; |
| bdrv->bconf.level_mid_mapping = 128; |
| bdrv->bconf.level_min = 10; |
| bdrv->bconf.level_max = 255; |
| bdrv->bconf.power_on_delay = 100; |
| bdrv->bconf.power_off_delay = 30; |
| bdrv->bconf.method = BL_CTRL_MAX; |
| ret = bl_config_load(bdrv, bdrv->pdev); |
| if (ret) |
| goto err; |
| |
| memset(&props, 0, sizeof(struct backlight_properties)); |
| props.type = BACKLIGHT_RAW; |
| props.power = FB_BLANK_UNBLANK; /* full on */ |
| props.max_brightness = (bdrv->bconf.level_max > 0 ? |
| bdrv->bconf.level_max : BL_LEVEL_MAX); |
| props.brightness = bdrv->level_init_on; |
| |
| if (index == 0) |
| sprintf(bl_name, "aml-bl"); |
| else |
| sprintf(bl_name, "aml-bl%d", index); |
| bldev = backlight_device_register(bl_name, &bdrv->pdev->dev, |
| bdrv, &aml_bl_ops, &props); |
| if (IS_ERR(bldev)) { |
| BLERR("[%d]: failed to register backlight\n", index); |
| ret = PTR_ERR(bldev); |
| goto err; |
| } |
| bdrv->bldev = bldev; |
| |
| bl_metrics_conf = &bdrv->bl_metrics_conf; |
| bl_metrics_conf->times = 60; |
| bl_metrics_conf->cnt = 0; |
| bl_metrics_conf->sum_cnt = 0; |
| bl_metrics_conf->level_count = 0; |
| bl_metrics_conf->brightness_count = 0; |
| bl_metrics_conf->frame_rate = 60; |
| bl_metrics_conf->level_buf = kcalloc(BL_LEVEL_CNT_MAX * 2, |
| sizeof(unsigned int), GFP_KERNEL); |
| if (!bl_metrics_conf->level_buf) |
| goto err; |
| |
| bl_metrics_conf->brightness_buf = kcalloc(BL_LEVEL_CNT_MAX * 2, |
| sizeof(unsigned int), GFP_KERNEL); |
| if (!bl_metrics_conf->brightness_buf) { |
| kfree(bl_metrics_conf->level_buf); |
| bl_metrics_conf->level_buf = NULL; |
| goto err; |
| } |
| |
| memset(bl_metrics_conf->level_buf, 0, |
| (sizeof(unsigned int)) * BL_LEVEL_CNT_MAX * 2); |
| bdrv->probe_done = 1; |
| |
| /* init workqueue */ |
| INIT_DELAYED_WORK(&bdrv->delayed_on_work, bl_delayd_on); |
| |
| bl_init_status_update(bdrv); |
| |
| bl_vsync_irq_init(bdrv); |
| bl_debug_file_creat(bdrv); |
| |
| BLPR("[%d]: %s: ok\n", index, __func__); |
| return; |
| |
| err: |
| /* free drvdata */ |
| platform_set_drvdata(bdrv->pdev, NULL); |
| /* free drv */ |
| kfree(bdrv); |
| bl_drv[index] = NULL; |
| bl_drv_init_state &= ~(1 << index); |
| BLPR("[%d]: %s: failed\n", index, __func__); |
| } |
| |
| int aml_bl_index_add(int drv_index, int conf_index) |
| { |
| if (drv_index >= LCD_MAX_DRV) { |
| BLERR("%s: invalid drv_index: %d\n", __func__, drv_index); |
| return -1; |
| } |
| |
| bl_index_lut[drv_index] = conf_index; |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) { |
| BLPR("%s: drv_index %d, config index: %d\n", |
| __func__, drv_index, conf_index); |
| } |
| return 0; |
| } |
| |
| static int aml_bl_probe(struct platform_device *pdev) |
| { |
| struct aml_bl_drv_s *bdrv; |
| const struct of_device_id *match; |
| int index = 0; |
| int ret; |
| |
| bl_global_init_once(); |
| |
| if (!pdev->dev.of_node) |
| return -1; |
| ret = of_property_read_u32(pdev->dev.of_node, "index", &index); |
| if (ret) { |
| if (lcd_debug_print_flag & LCD_DBG_PR_BL_NORMAL) |
| BLPR("%s: no index exist, default to 0\n", __func__); |
| index = 0; |
| } |
| if (index >= LCD_MAX_DRV) { |
| BLERR("%s: invalid index %d\n", __func__, index); |
| return -1; |
| } |
| if (bl_drv_init_state & (1 << index)) { |
| BLERR("%s: index %d driver already registered\n", |
| __func__, index); |
| return -1; |
| } |
| bl_drv_init_state |= (1 << index); |
| |
| match = of_match_device(bl_dt_match_table, &pdev->dev); |
| if (!match) { |
| BLERR("%s: no match table\n", __func__); |
| goto aml_bl_probe_err; |
| } |
| |
| bdrv = kzalloc(sizeof(*bdrv), GFP_KERNEL); |
| if (!bdrv) |
| goto aml_bl_probe_err; |
| bdrv->index = index; |
| bdrv->data = (struct bl_data_s *)match->data; |
| bl_drv[index] = bdrv; |
| BLPR("chip type: (%d-%s)\n", |
| bdrv->data->chip_type, |
| bdrv->data->chip_name); |
| |
| /* set drvdata */ |
| platform_set_drvdata(pdev, bdrv); |
| bl_cdev_add(bdrv, &pdev->dev); |
| bdrv->pdev = pdev; |
| |
| bl_pwm_init_config_probe(bdrv->data); |
| |
| INIT_WORK(&bdrv->config_probe_work, aml_bl_config_probe_work); |
| lcd_queue_work(&bdrv->config_probe_work); |
| |
| BLPR("[%d]: probe OK, init_state:0x%x\n", index, bl_drv_init_state); |
| return 0; |
| |
| aml_bl_probe_err: |
| bl_drv_init_state &= ~(1 << index); |
| BLPR("[%d]: %s failed\n", index, __func__); |
| return -1; |
| } |
| |
| static int __exit aml_bl_remove(struct platform_device *pdev) |
| { |
| struct aml_bl_drv_s *bdrv = platform_get_drvdata(pdev); |
| int index; |
| |
| if (!bdrv) |
| return 0; |
| |
| index = bdrv->index; |
| |
| kfree(bdrv->bl_metrics_conf.level_buf); |
| kfree(bdrv->bl_metrics_conf.brightness_buf); |
| cancel_delayed_work_sync(&bdrv->delayed_on_work); |
| backlight_device_unregister(bdrv->bldev); |
| |
| bl_debug_file_remove(bdrv); |
| bl_vsync_irq_remove(bdrv); |
| |
| /* free drvdata */ |
| platform_set_drvdata(pdev, NULL); |
| bl_cdev_remove(bdrv); |
| |
| #ifdef CONFIG_AMLOGIC_BL_LDIM |
| if (bdrv->bconf.ldim_flag) |
| aml_ldim_remove(); |
| #endif |
| /* platform_set_drvdata(pdev, NULL); */ |
| switch (bdrv->bconf.method) { |
| case BL_CTRL_PWM: |
| kfree(bdrv->bconf.bl_pwm); |
| break; |
| case BL_CTRL_PWM_COMBO: |
| kfree(bdrv->bconf.bl_pwm_combo0); |
| kfree(bdrv->bconf.bl_pwm_combo1); |
| break; |
| default: |
| break; |
| } |
| kfree(bdrv); |
| bl_drv[index] = NULL; |
| bl_drv_init_state &= ~(1 << index); |
| bl_global_remove_once(); |
| |
| return 0; |
| } |
| |
| static struct platform_driver aml_bl_driver = { |
| .driver = { |
| .name = AML_BL_NAME, |
| .owner = THIS_MODULE, |
| #ifdef CONFIG_OF |
| .of_match_table = of_match_ptr(bl_dt_match_table), |
| #endif |
| }, |
| .probe = aml_bl_probe, |
| .remove = __exit_p(aml_bl_remove), |
| #ifdef CONFIG_PM |
| .suspend = aml_bl_suspend, |
| .resume = aml_bl_resume, |
| #endif |
| }; |
| |
| int __init aml_bl_init(void) |
| { |
| if (platform_driver_register(&aml_bl_driver)) { |
| BLPR("failed to register bl driver module\n"); |
| return -ENODEV; |
| } |
| return 0; |
| } |
| |
| void __exit aml_bl_exit(void) |
| { |
| platform_driver_unregister(&aml_bl_driver); |
| } |
| |
| static int aml_bl_level_setup(char *str) |
| { |
| int ret = 0; |
| |
| if (str) { |
| ret = kstrtouint(str, 10, &bl_level[0]); |
| BLPR("bl_level: %d\n", bl_level[0]); |
| } |
| |
| return ret; |
| } |
| __setup("bl_level=", aml_bl_level_setup); |
| |
| //MODULE_DESCRIPTION("AML Backlight Driver"); |
| //MODULE_LICENSE("GPL"); |
| //MODULE_AUTHOR("Amlogic, Inc."); |