// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2020 MediaTek Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include "backlight_cooling.h" /*================================================== * cooler callback functions *================================================== */ static int backlight_throttle(struct backlight_cooling_device *backlight_cdev, unsigned long state) { struct device *dev = backlight_cdev->dev; unsigned long bl_percent; int enable = (state == BACKLIGHT_COOLING_UNLIMITED_STATE) ? 0 : 1; bl_percent = BACKLIGHT_COOLING_MAX_STATE - state; setMaxBrightness(backlight_cdev->name, bl_percent, enable); backlight_cdev->target_state = state; dev_info(dev, "%s: set lv = %ld, bl percent = %ld done\n", backlight_cdev->name, state, bl_percent); return 0; } static int backlight_cooling_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state) { struct backlight_cooling_device *backlight_cdev = cdev->devdata; *state = backlight_cdev->max_state; return 0; } static int backlight_cooling_get_cur_state(struct thermal_cooling_device *cdev, unsigned long *state) { struct backlight_cooling_device *backlight_cdev = cdev->devdata; *state = backlight_cdev->target_state; return 0; } static int backlight_cooling_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) { struct backlight_cooling_device *backlight_cdev = cdev->devdata; int ret; /* Request state should be less than max_state */ if (WARN_ON(state > backlight_cdev->max_state || !backlight_cdev->throttle)) return -EINVAL; if (backlight_cdev->target_state == state) return 0; ret = backlight_cdev->throttle(backlight_cdev, state); return ret; } /*================================================== * platform data and platform driver callbacks *================================================== */ static const struct of_device_id backlight_cooling_of_match[] = { { .compatible = "mediatek,backlight-cooler", }, {}, }; MODULE_DEVICE_TABLE(of, backlight_cooling_of_match); static struct thermal_cooling_device_ops backlight_cooling_ops = { .get_max_state = backlight_cooling_get_max_state, .get_cur_state = backlight_cooling_get_cur_state, .set_cur_state = backlight_cooling_set_cur_state, }; static int init_bl_cooling_device(struct device *dev, struct backlight_cooling_device *bl_cdev) { struct thermal_cooling_device *cdev; bl_cdev->target_state = BACKLIGHT_COOLING_UNLIMITED_STATE; bl_cdev->max_state = BACKLIGHT_COOLING_MAX_STATE; bl_cdev->throttle = backlight_throttle; bl_cdev->dev = dev; cdev = thermal_of_cooling_device_register(dev->of_node, bl_cdev->name, bl_cdev, &backlight_cooling_ops); if (IS_ERR(cdev)) goto init_fail; bl_cdev->cdev = cdev; dev_info(dev, "register %s done\n", bl_cdev->name); return 0; init_fail: return -EINVAL; } static int parse_dt(struct platform_device *pdev, struct backlight_cooling_device *bl_cdev) { struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; const char *bl_names = NULL; int ret; ret = of_property_read_string(np, "backlight-names", &(bl_names)); if (ret) { dev_notice(dev, "failed to parse cooler name from DT!\n"); return ret; } snprintf(bl_cdev->name, MAX_BACKLIGHT_COOLER_NAME_LEN, "%s", bl_names); return 0; } static int backlight_cooling_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct backlight_cooling_device *bl_cdev; int ret = 0; bl_cdev = devm_kzalloc(dev, sizeof(*bl_cdev), GFP_KERNEL); if (!bl_cdev) return -ENOMEM; ret = parse_dt(pdev, bl_cdev); if (ret) { dev_notice(dev, "failed to parse cooler nodes from DT!\n"); return ret; } ret = init_bl_cooling_device(dev, bl_cdev); if (ret) { dev_notice(dev, "failed to init backlight cooler!\n"); return ret; } platform_set_drvdata(pdev, bl_cdev); return 0; } static int backlight_cooling_remove(struct platform_device *pdev) { struct backlight_cooling_device *bl_cdev; bl_cdev = (struct backlight_cooling_device *)platform_get_drvdata(pdev); thermal_cooling_device_unregister(bl_cdev->cdev); platform_set_drvdata(pdev, NULL); return 0; } static struct platform_driver backlight_cooling_driver = { .probe = backlight_cooling_probe, .remove = backlight_cooling_remove, .driver = { .name = "mtk-backlight-cooling", .of_match_table = backlight_cooling_of_match, }, }; module_platform_driver(backlight_cooling_driver); MODULE_AUTHOR("Ming-Hao Chou "); MODULE_DESCRIPTION("Mediatek backlight cooling driver"); MODULE_LICENSE("GPL v2");