623 lines
18 KiB
C
Executable file
623 lines
18 KiB
C
Executable file
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2021 MediaTek Inc.
|
|
*
|
|
*/
|
|
|
|
#include <linux/ctype.h>
|
|
#include <linux/err.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/leds.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/sched/clock.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/string.h>
|
|
#include <leds-mtk.h>
|
|
|
|
|
|
/****************************************************************************
|
|
* variables
|
|
***************************************************************************/
|
|
#undef pr_fmt
|
|
#define pr_fmt(fmt) KBUILD_MODNAME " %s(%d) :" fmt, __func__, __LINE__
|
|
|
|
//prize add by wangfei for backlight not close when first set backlight 20230116 start
|
|
#define DEF_HW_BRINGHTNESS 1
|
|
//prize add by wangfei for backlight not close when first set backlight 20230116 end
|
|
|
|
static int mtk_set_brightness(struct led_classdev *led_cdev,
|
|
enum led_brightness brightness);
|
|
|
|
struct mt_leds_desp_info {
|
|
int lens;
|
|
struct led_desp *leds[0];
|
|
};
|
|
|
|
static DEFINE_MUTEX(leds_mutex);
|
|
static BLOCKING_NOTIFIER_HEAD(mtk_leds_chain_head);
|
|
|
|
struct mt_leds_desp_info *leds_info;
|
|
|
|
int mtk_leds_register_notifier(struct notifier_block *nb)
|
|
{
|
|
return blocking_notifier_chain_register(&mtk_leds_chain_head, nb);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mtk_leds_register_notifier);
|
|
|
|
int mtk_leds_unregister_notifier(struct notifier_block *nb)
|
|
{
|
|
return blocking_notifier_chain_unregister(&mtk_leds_chain_head, nb);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mtk_leds_unregister_notifier);
|
|
|
|
int mt_leds_call_notifier(unsigned long action, void *data)
|
|
{
|
|
return blocking_notifier_call_chain(&mtk_leds_chain_head, action, data);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt_leds_call_notifier);
|
|
|
|
static int __maybe_unused call_notifier(int event, struct led_conf_info *led_conf)
|
|
{
|
|
int err = 0;
|
|
|
|
if (led_conf->flags & LED_MT_BRIGHTNESS_CHANGED) {
|
|
err = mt_leds_call_notifier(event, led_conf);
|
|
if (err)
|
|
pr_info("Error notifier_call_chain error\n");
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static ssize_t min_brightness_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
|
struct led_conf_info *led_conf =
|
|
container_of(led_cdev, struct led_conf_info, cdev);
|
|
|
|
return sprintf(buf, "%u\n", led_conf->min_brightness);
|
|
}
|
|
static DEVICE_ATTR_RO(min_brightness);
|
|
|
|
static ssize_t max_hw_brightness_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
|
struct led_conf_info *led_conf =
|
|
container_of(led_cdev, struct led_conf_info, cdev);
|
|
|
|
return sprintf(buf, "%u\n", led_conf->max_hw_brightness);
|
|
}
|
|
static DEVICE_ATTR_RO(max_hw_brightness);
|
|
|
|
static ssize_t min_hw_brightness_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
|
struct led_conf_info *led_conf =
|
|
container_of(led_cdev, struct led_conf_info, cdev);
|
|
|
|
return sprintf(buf, "%u\n", led_conf->min_hw_brightness);
|
|
}
|
|
static DEVICE_ATTR_RO(min_hw_brightness);
|
|
|
|
static ssize_t led_mode_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
|
struct led_conf_info *led_conf =
|
|
container_of(led_cdev, struct led_conf_info, cdev);
|
|
|
|
return sprintf(buf, "%u\n", led_conf->mode);
|
|
}
|
|
static DEVICE_ATTR_RO(led_mode);
|
|
static ssize_t connector_id_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
|
struct led_conf_info *led_conf =
|
|
container_of(led_cdev, struct led_conf_info, cdev);
|
|
|
|
|
|
if (led_conf->connector_id <= 0) {
|
|
struct mt_led_data *led_dat =
|
|
container_of(led_conf, struct mt_led_data, conf);
|
|
led_dat->mtk_conn_id_get(led_dat, led_dat->desp.index);
|
|
}
|
|
|
|
return sprintf(buf, "%u\n", led_conf->connector_id);
|
|
}
|
|
static DEVICE_ATTR_RO(connector_id);
|
|
|
|
#ifdef CONFIG_LEDS_MT_BRIGHTNESS_HW_CHANGED
|
|
static ssize_t mt_brightness_hw_changed_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
|
struct led_conf_info *led_conf =
|
|
container_of(led_cdev, struct led_conf_info, cdev);
|
|
|
|
if (led_conf->brightness_hw_changed == -1)
|
|
return -ENODATA;
|
|
|
|
return sprintf(buf, "%u\n", led_conf->brightness_hw_changed);
|
|
}
|
|
|
|
static DEVICE_ATTR_RO(mt_brightness_hw_changed);
|
|
|
|
static int mt_leds_add_brightness_hw_changed(struct led_conf_info *led_conf)
|
|
{
|
|
struct device *dev = led_conf->cdev.dev;
|
|
int ret;
|
|
|
|
ret = device_create_file(dev, &dev_attr_mt_brightness_hw_changed);
|
|
if (ret) {
|
|
pr_info("Error creating mt_brightness_hw_changed\n");
|
|
return ret;
|
|
}
|
|
|
|
led_conf->brightness_hw_changed_kn =
|
|
sysfs_get_dirent(dev->kobj.sd, "mt_brightness_hw_changed");
|
|
if (!led_conf->brightness_hw_changed_kn) {
|
|
pr_info("Error getting mt_brightness_hw_changed kn\n");
|
|
device_remove_file(dev, &dev_attr_mt_brightness_hw_changed);
|
|
return -ENXIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void mt_leds_remove_brightness_hw_changed(struct led_conf_info *led_conf)
|
|
{
|
|
sysfs_put(led_conf->brightness_hw_changed_kn);
|
|
device_remove_file(led_conf->cdev.dev, &dev_attr_mt_brightness_hw_changed);
|
|
}
|
|
|
|
void mt_leds_notify_brightness_hw_changed(struct led_conf_info *led_conf,
|
|
enum led_brightness brightness)
|
|
{
|
|
if (WARN_ON(!led_conf->brightness_hw_changed_kn))
|
|
return;
|
|
|
|
led_conf->brightness_hw_changed = brightness;
|
|
sysfs_notify_dirent(led_conf->brightness_hw_changed_kn);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt_leds_notify_brightness_hw_changed);
|
|
#else
|
|
static int mt_leds_add_brightness_hw_changed(struct led_conf_info *led_conf)
|
|
{
|
|
return 0;
|
|
}
|
|
static void mt_leds_remove_brightness_hw_changed(struct led_conf_info *led_conf)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* DEBUG MACROS
|
|
***************************************************************************/
|
|
|
|
static void led_debug_log(struct mt_led_data *s_led,
|
|
int level, int mappingLevel)
|
|
{
|
|
unsigned long cur_time_mod = 0;
|
|
unsigned long long cur_time_display = 0;
|
|
int ret = 0;
|
|
|
|
s_led->debug.current_t = sched_clock();
|
|
cur_time_display = s_led->debug.current_t;
|
|
do_div(cur_time_display, 1000000);
|
|
cur_time_mod = do_div(cur_time_display, 1000);
|
|
|
|
ret = snprintf(s_led->debug.buffer + strlen(s_led->debug.buffer),
|
|
4095 - strlen(s_led->debug.buffer),
|
|
"T:%lld.%ld,L:%d L:%d map:%d ",
|
|
cur_time_display, cur_time_mod,
|
|
s_led->conf.cdev.brightness, level, mappingLevel);
|
|
|
|
s_led->debug.count++;
|
|
|
|
if (ret < 0 || ret >= 4096) {
|
|
pr_info("print log error!");
|
|
s_led->debug.count = 5;
|
|
}
|
|
|
|
if (level == 0 || s_led->debug.count >= 5 ||
|
|
(s_led->debug.current_t - s_led->debug.last_t) > 1000000000) {
|
|
pr_info("%s", s_led->debug.buffer);
|
|
s_led->debug.count = 0;
|
|
s_led->debug.buffer[strlen("[Light] Set directly ") +
|
|
strlen(s_led->conf.cdev.name)] = '\0';
|
|
}
|
|
|
|
s_led->debug.last_t = sched_clock();
|
|
}
|
|
|
|
static int get_desp_index(char *name)
|
|
{
|
|
int i = 0;
|
|
|
|
while (i < leds_info->lens) {
|
|
if (!strcmp(name, leds_info->leds[i]->name))
|
|
return i;
|
|
i++;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* driver functions
|
|
***************************************************************************/
|
|
|
|
static int brightness_maptolevel(struct led_conf_info *led_conf, int brightness)
|
|
{
|
|
return (((led_conf->max_hw_brightness) * brightness
|
|
+ ((led_conf->cdev.max_brightness) / 2))
|
|
/ (led_conf->cdev.max_brightness));
|
|
}
|
|
|
|
static int mtk_set_hw_brightness(struct mt_led_data *led_dat, int brightness,
|
|
unsigned int params, unsigned int params_flag)
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
if (brightness != 0) {
|
|
brightness = min(brightness, led_dat->conf.limit_hw_brightness);
|
|
brightness = max(brightness, led_dat->conf.min_hw_brightness);
|
|
}
|
|
|
|
if (brightness == led_dat->hw_brightness && params_flag == (1 << SET_BACKLIGHT_LEVEL))
|
|
return 0;
|
|
pr_debug("set hw brightness(%s): %d -> %d",
|
|
led_dat->conf.cdev.name, led_dat->hw_brightness, brightness);
|
|
ret = led_dat->mtk_hw_brightness_set(led_dat, brightness, params, params_flag);
|
|
if (ret >= 0) {
|
|
if (brightness != led_dat->hw_brightness &&
|
|
(params_flag & (1 << SET_BACKLIGHT_LEVEL))) {
|
|
led_dat->hw_brightness = brightness;
|
|
#ifdef CONFIG_LEDS_MT_BRIGHTNESS_HW_CHANGED
|
|
mt_leds_notify_brightness_hw_changed(&led_dat->conf, brightness);
|
|
#endif
|
|
}
|
|
} else {
|
|
pr_debug("set hw brightness: %d -> %d, params: %d, params_flag: %d failed!",
|
|
led_dat->hw_brightness, brightness, params, params_flag);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mtk_leds_brightness_set(char *name, int level,
|
|
unsigned int params, unsigned int params_flag)
|
|
{
|
|
struct mt_led_data *led_dat;
|
|
int index;
|
|
|
|
index = get_desp_index(name);
|
|
if (index < 0) {
|
|
pr_notice("can not find leds by led_desp %s", name);
|
|
return -1;
|
|
}
|
|
led_dat = container_of(leds_info->leds[index],
|
|
struct mt_led_data, desp);
|
|
|
|
mutex_lock(&led_dat->led_access);
|
|
if (!led_dat->conf.aal_enable) {
|
|
//pr_debug("aal not enable, set %s %d return", name, level);
|
|
} else {
|
|
mtk_set_hw_brightness(led_dat, level, params, params_flag);
|
|
led_dat->last_hw_brightness = level;
|
|
}
|
|
mutex_unlock(&led_dat->led_access);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mtk_leds_brightness_set);
|
|
|
|
static int mtk_set_brightness(struct led_classdev *led_cdev,
|
|
enum led_brightness brightness)
|
|
{
|
|
int trans_level = 0;
|
|
|
|
struct led_conf_info *led_conf =
|
|
container_of(led_cdev, struct led_conf_info, cdev);
|
|
struct mt_led_data *led_dat =
|
|
container_of(led_conf, struct mt_led_data, conf);
|
|
|
|
if (led_conf->connector_id <= 0) {
|
|
struct mt_led_data *led_dat =
|
|
container_of(led_conf, struct mt_led_data, conf);
|
|
led_dat->mtk_conn_id_get(led_dat, led_dat->desp.index);
|
|
}
|
|
|
|
if (led_dat->last_brightness == brightness)
|
|
return 0;
|
|
|
|
led_dat->last_brightness = brightness;
|
|
|
|
trans_level = brightness_maptolevel(led_conf, brightness);
|
|
|
|
led_debug_log(led_dat, brightness, trans_level);
|
|
|
|
call_notifier(LED_BRIGHTNESS_CHANGED, led_conf);
|
|
mutex_lock(&led_dat->led_access);
|
|
if (!led_conf->aal_enable) {
|
|
mtk_set_hw_brightness(led_dat, trans_level, 0, 0);
|
|
led_dat->last_hw_brightness = trans_level;
|
|
}
|
|
mutex_unlock(&led_dat->led_access);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
* add API for temperature control
|
|
***************************************************************************/
|
|
|
|
int setMaxBrightness(char *name, int percent, bool enable)
|
|
{
|
|
struct mt_led_data *led_dat;
|
|
int max_l = 0, index = -1, limit_l = 0, cur_l = 0;
|
|
|
|
index = get_desp_index(name);
|
|
if (index < 0) {
|
|
pr_notice("can not find leds by led_desp %s", name);
|
|
return -1;
|
|
}
|
|
led_dat = container_of(leds_info->leds[index],
|
|
struct mt_led_data, desp);
|
|
|
|
max_l = led_dat->conf.max_hw_brightness;
|
|
limit_l = (percent * max_l) / 100;
|
|
pr_info("before: name: %s, percent : %d, limit_l : %d, enable: %d",
|
|
leds_info->leds[index]->name, percent, limit_l, enable);
|
|
if (enable) {
|
|
led_dat->conf.limit_hw_brightness = limit_l;
|
|
cur_l = min(led_dat->last_hw_brightness, limit_l);
|
|
} else if (!enable) {
|
|
led_dat->conf.limit_hw_brightness = max_l;
|
|
cur_l = led_dat->last_hw_brightness;
|
|
}
|
|
|
|
if (led_dat->conf.cdev.brightness != 0)
|
|
mtk_set_hw_brightness(led_dat, cur_l, 0, 0);
|
|
|
|
pr_info("after: name: %s, cur_l : %d, max_brightness : %d",
|
|
led_dat->conf.cdev.name, cur_l, led_dat->conf.limit_hw_brightness);
|
|
return 0;
|
|
|
|
}
|
|
EXPORT_SYMBOL(setMaxBrightness);
|
|
|
|
int mt_leds_parse_dt(struct mt_led_data *mdev, struct fwnode_handle *fwnode)
|
|
{
|
|
int ret = 0;
|
|
const char *state;
|
|
struct mt_leds_desp_info *nleds_info;
|
|
/* prize liuyong, modify boot up backlight, 20231205, start*/
|
|
unsigned int default_brightness;
|
|
/* prize liuyong, modify boot up backlight, 20231205, end*/
|
|
|
|
ret = fwnode_property_read_string(fwnode, "label", &(mdev->conf.cdev.name));
|
|
if (ret)
|
|
return -EINVAL;
|
|
|
|
ret = fwnode_property_read_string(fwnode, "default-trigger",
|
|
&(mdev->conf.cdev.default_trigger));
|
|
if (ret) {
|
|
pr_info("Fail to read default-trigger property");
|
|
mdev->conf.cdev.default_trigger = NULL;
|
|
}
|
|
|
|
ret = fwnode_property_read_u32(fwnode,
|
|
"max-hw-brightness", &(mdev->conf.max_hw_brightness));
|
|
if (ret) {
|
|
pr_info("No max-hw-brightness, use default value 2047");
|
|
mdev->conf.max_hw_brightness = 2047;
|
|
}
|
|
|
|
ret = fwnode_property_read_u32(fwnode,
|
|
"max-brightness", &(mdev->conf.cdev.max_brightness));
|
|
if (ret) {
|
|
pr_info("No max-brightness, use max_hw_brightness");
|
|
mdev->conf.cdev.max_brightness = mdev->conf.max_hw_brightness;
|
|
}
|
|
|
|
ret = fwnode_property_read_u32(fwnode,
|
|
"min-hw-brightness", &(mdev->conf.min_hw_brightness));
|
|
if (ret) {
|
|
pr_info("No min-hw-brightness, use default value 1");
|
|
mdev->conf.min_hw_brightness = 1;
|
|
}
|
|
ret = fwnode_property_read_u32(fwnode,
|
|
"min-brightness", &(mdev->conf.min_brightness));
|
|
if (ret) {
|
|
pr_info("No min-brightness, use min_hw_brightness");
|
|
mdev->conf.min_brightness = mdev->conf.min_hw_brightness;
|
|
}
|
|
ret = fwnode_property_read_u32(fwnode,
|
|
"led_mode", &(mdev->conf.mode));
|
|
if (ret) {
|
|
pr_info("No min-brightness, use default value 1");
|
|
mdev->conf.mode = MT_LED_MODE_CUST_BLS_I2C;
|
|
}
|
|
mdev->conf.limit_hw_brightness = mdev->conf.max_hw_brightness;
|
|
ret = fwnode_property_read_string(fwnode, "default-state", &state);
|
|
if (!ret) {
|
|
if (!strncmp(state, "half", strlen("half")))
|
|
mdev->conf.cdev.brightness = mdev->conf.cdev.max_brightness / 2;
|
|
else if (!strncmp(state, "on", strlen("on")))
|
|
mdev->conf.cdev.brightness = mdev->conf.cdev.max_brightness;
|
|
else
|
|
mdev->conf.cdev.brightness = 0;
|
|
} else {
|
|
/* prize liuyong, modify boot up backlight, 20231205, start*/
|
|
ret = fwnode_property_read_u32(fwnode,
|
|
"default-brightness", &default_brightness);
|
|
if (!ret) {
|
|
pr_info("use default-brightness: %d value", default_brightness);
|
|
mdev->conf.cdev.brightness = default_brightness;
|
|
} else
|
|
mdev->conf.cdev.brightness = mdev->conf.cdev.max_brightness * 40 / 100;
|
|
/* prize liuyong, modify boot up backlight, 20231205, end*/
|
|
}
|
|
|
|
strscpy(mdev->desp.name, mdev->conf.cdev.name,
|
|
sizeof(mdev->desp.name));
|
|
mdev->desp.index = leds_info->lens;
|
|
|
|
mdev->conf.connector_id = -1;
|
|
|
|
nleds_info = krealloc(leds_info, sizeof(struct mt_leds_desp_info) +
|
|
sizeof(struct led_desp *) * (leds_info->lens + 1),
|
|
GFP_KERNEL);
|
|
|
|
if (!nleds_info) {
|
|
kfree(nleds_info);
|
|
return -ENOMEM;
|
|
}
|
|
leds_info = nleds_info;
|
|
leds_info->leds[leds_info->lens] = &mdev->desp;
|
|
leds_info->lens++;
|
|
mdev->conf.aal_enable = 0;
|
|
mutex_init(&mdev->led_access);
|
|
|
|
pr_info("parse led: %s, num: %d, max: %d, min: %d, max_hw: %d, min_hw: %d, brightness: %d",
|
|
mdev->conf.cdev.name,
|
|
leds_info->lens,
|
|
mdev->conf.connector_id,
|
|
mdev->conf.cdev.max_brightness,
|
|
mdev->conf.min_brightness,
|
|
mdev->conf.max_hw_brightness,
|
|
mdev->conf.min_hw_brightness,
|
|
mdev->conf.cdev.brightness);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt_leds_parse_dt);
|
|
|
|
|
|
static struct attribute *led_class_attrs[] = {
|
|
&dev_attr_min_brightness.attr,
|
|
&dev_attr_max_hw_brightness.attr,
|
|
&dev_attr_min_hw_brightness.attr,
|
|
&dev_attr_led_mode.attr,
|
|
&dev_attr_connector_id.attr,
|
|
NULL,
|
|
};
|
|
|
|
static const struct attribute_group mt_led_group = {
|
|
.attrs = led_class_attrs,
|
|
};
|
|
|
|
static const struct attribute_group *mt_led_groups[] = {
|
|
&mt_led_group,
|
|
NULL,
|
|
};
|
|
|
|
int mt_leds_classdev_register(struct device *parent,
|
|
struct mt_led_data *led_dat)
|
|
{
|
|
int ret = 0;
|
|
|
|
led_dat->conf.cdev.flags = LED_CORE_SUSPENDRESUME;
|
|
led_dat->conf.flags = LED_MT_BRIGHTNESS_HW_CHANGED | LED_MT_BRIGHTNESS_CHANGED;
|
|
led_dat->conf.cdev.brightness_set_blocking = mtk_set_brightness;
|
|
#ifdef CONFIG_LEDS_MT_BRIGHTNESS_HW_CHANGED
|
|
led_dat->conf.brightness_hw_changed = -1;
|
|
#endif
|
|
led_dat->conf.cdev.groups = mt_led_groups;
|
|
|
|
ret = devm_led_classdev_register(parent, &(led_dat->conf.cdev));
|
|
if (ret < 0) {
|
|
pr_notice("led class register fail!");
|
|
return ret;
|
|
}
|
|
pr_info("%s devm_led_classdev_register ok! ", led_dat->conf.cdev.name);
|
|
if (led_dat->conf.flags & LED_MT_BRIGHTNESS_HW_CHANGED) {
|
|
ret = mt_leds_add_brightness_hw_changed(&led_dat->conf);
|
|
if (ret) {
|
|
pr_info("%s add_brightness_hw_changed failed! ", led_dat->conf.cdev.name);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
ret = snprintf(led_dat->debug.buffer + strlen(led_dat->debug.buffer),
|
|
4095 - strlen(led_dat->debug.buffer),
|
|
"[Light] Set %s directly ", led_dat->conf.cdev.name);
|
|
|
|
if (ret < 0 || ret >= 4096)
|
|
pr_info("print log init error!");
|
|
|
|
led_dat->last_brightness = led_dat->conf.cdev.brightness;
|
|
|
|
mtk_set_hw_brightness(led_dat,
|
|
brightness_maptolevel(&led_dat->conf, led_dat->last_brightness), 0, 0);
|
|
//prize add by wangfei for backlight not close when first set backlight 20230116 start
|
|
led_dat->hw_brightness = DEF_HW_BRINGHTNESS;
|
|
//prize add by wangfei for backlight not close when first set backlight 20230116 end
|
|
pr_info("%s devm_led_classdev_register end! ", led_dat->conf.cdev.name);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt_leds_classdev_register);
|
|
|
|
void mt_leds_classdev_unregister(struct device *parent,
|
|
struct mt_led_data *led_dat)
|
|
{
|
|
devm_led_classdev_unregister(parent, &(led_dat->conf.cdev));
|
|
if (led_dat->conf.flags & LED_MT_BRIGHTNESS_HW_CHANGED)
|
|
mt_leds_remove_brightness_hw_changed(&(led_dat->conf));
|
|
|
|
pr_info("%s devm_led_classdev_unregister ok! ", led_dat->conf.cdev.name);
|
|
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt_leds_classdev_unregister);
|
|
|
|
static int __init mtk_leds_init(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
pr_info("init begain +++");
|
|
|
|
leds_info = kzalloc(sizeof(struct mt_leds_desp_info), GFP_KERNEL);
|
|
|
|
if (!leds_info) {
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
|
|
pr_info("init end ---");
|
|
return 0;
|
|
err:
|
|
pr_notice("Failed to probe!\n");
|
|
ret = -ENOMEM;
|
|
return ret;
|
|
|
|
}
|
|
|
|
static void __exit mtk_leds_exit(void)
|
|
{
|
|
kfree(leds_info);
|
|
leds_info = NULL;
|
|
}
|
|
|
|
/* delay leds init, for (1)display has delayed to use clock upstream.
|
|
* (2)to fix repeat switch battary and power supply caused BL KE issue,
|
|
* battary calling bl .shutdown whitch need to call display
|
|
* function and they not yet probe.
|
|
*/
|
|
module_init(mtk_leds_init);
|
|
module_exit(mtk_leds_exit);
|
|
|
|
MODULE_AUTHOR("Mediatek Corporation");
|
|
MODULE_DESCRIPTION("MTK Disp Backlight Driver");
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|