162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Miscellaneous procedures for dealing with the PowerMac hardware. 462306a36Sopenharmony_ci * Contains support for the backlight. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2000 Benjamin Herrenschmidt 762306a36Sopenharmony_ci * Copyright (C) 2006 Michael Hanselmann <linux-kernel@hansmi.ch> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/fb.h> 1362306a36Sopenharmony_ci#include <linux/backlight.h> 1462306a36Sopenharmony_ci#include <linux/adb.h> 1562306a36Sopenharmony_ci#include <linux/pmu.h> 1662306a36Sopenharmony_ci#include <linux/atomic.h> 1762306a36Sopenharmony_ci#include <linux/export.h> 1862306a36Sopenharmony_ci#include <asm/backlight.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define OLD_BACKLIGHT_MAX 15 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic void pmac_backlight_key_worker(struct work_struct *work); 2362306a36Sopenharmony_cistatic void pmac_backlight_set_legacy_worker(struct work_struct *work); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic DECLARE_WORK(pmac_backlight_key_work, pmac_backlight_key_worker); 2662306a36Sopenharmony_cistatic DECLARE_WORK(pmac_backlight_set_legacy_work, pmac_backlight_set_legacy_worker); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* Although these variables are used in interrupt context, it makes no sense to 2962306a36Sopenharmony_ci * protect them. No user is able to produce enough key events per second and 3062306a36Sopenharmony_ci * notice the errors that might happen. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_cistatic int pmac_backlight_key_queued; 3362306a36Sopenharmony_cistatic int pmac_backlight_set_legacy_queued; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* The via-pmu code allows the backlight to be grabbed, in which case the 3662306a36Sopenharmony_ci * in-kernel control of the brightness needs to be disabled. This should 3762306a36Sopenharmony_ci * only be used by really old PowerBooks. 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_cistatic atomic_t kernel_backlight_disabled = ATOMIC_INIT(0); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* Protect the pmac_backlight variable below. 4262306a36Sopenharmony_ci You should hold this lock when using the pmac_backlight pointer to 4362306a36Sopenharmony_ci prevent its potential removal. */ 4462306a36Sopenharmony_ciDEFINE_MUTEX(pmac_backlight_mutex); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* Main backlight storage 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci * Backlight drivers in this variable are required to have the "ops" 4962306a36Sopenharmony_ci * attribute set and to have an update_status function. 5062306a36Sopenharmony_ci * 5162306a36Sopenharmony_ci * We can only store one backlight here, but since Apple laptops have only one 5262306a36Sopenharmony_ci * internal display, it doesn't matter. Other backlight drivers can be used 5362306a36Sopenharmony_ci * independently. 5462306a36Sopenharmony_ci * 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_cistruct backlight_device *pmac_backlight; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ciint pmac_has_backlight_type(const char *type) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct device_node* bk_node = of_find_node_by_name(NULL, "backlight"); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (bk_node) { 6362306a36Sopenharmony_ci const char *prop = of_get_property(bk_node, 6462306a36Sopenharmony_ci "backlight-control", NULL); 6562306a36Sopenharmony_ci if (prop && strncmp(prop, type, strlen(type)) == 0) { 6662306a36Sopenharmony_ci of_node_put(bk_node); 6762306a36Sopenharmony_ci return 1; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci of_node_put(bk_node); 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return 0; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ciint pmac_backlight_curve_lookup(struct fb_info *info, int value) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci int level = (FB_BACKLIGHT_LEVELS - 1); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (info && info->bl_dev) { 8062306a36Sopenharmony_ci int i, max = 0; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* Look for biggest value */ 8362306a36Sopenharmony_ci for (i = 0; i < FB_BACKLIGHT_LEVELS; i++) 8462306a36Sopenharmony_ci max = max((int)info->bl_curve[i], max); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* Look for nearest value */ 8762306a36Sopenharmony_ci for (i = 0; i < FB_BACKLIGHT_LEVELS; i++) { 8862306a36Sopenharmony_ci int diff = abs(info->bl_curve[i] - value); 8962306a36Sopenharmony_ci if (diff < max) { 9062306a36Sopenharmony_ci max = diff; 9162306a36Sopenharmony_ci level = i; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci return level; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic void pmac_backlight_key_worker(struct work_struct *work) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci if (atomic_read(&kernel_backlight_disabled)) 10362306a36Sopenharmony_ci return; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci mutex_lock(&pmac_backlight_mutex); 10662306a36Sopenharmony_ci if (pmac_backlight) { 10762306a36Sopenharmony_ci struct backlight_properties *props; 10862306a36Sopenharmony_ci int brightness; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci props = &pmac_backlight->props; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci brightness = props->brightness + 11362306a36Sopenharmony_ci ((pmac_backlight_key_queued?-1:1) * 11462306a36Sopenharmony_ci (props->max_brightness / 15)); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (brightness < 0) 11762306a36Sopenharmony_ci brightness = 0; 11862306a36Sopenharmony_ci else if (brightness > props->max_brightness) 11962306a36Sopenharmony_ci brightness = props->max_brightness; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci props->brightness = brightness; 12262306a36Sopenharmony_ci backlight_update_status(pmac_backlight); 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci mutex_unlock(&pmac_backlight_mutex); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/* This function is called in interrupt context */ 12862306a36Sopenharmony_civoid pmac_backlight_key(int direction) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci if (atomic_read(&kernel_backlight_disabled)) 13162306a36Sopenharmony_ci return; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* we can receive multiple interrupts here, but the scheduled work 13462306a36Sopenharmony_ci * will run only once, with the last value 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_ci pmac_backlight_key_queued = direction; 13762306a36Sopenharmony_ci schedule_work(&pmac_backlight_key_work); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic int __pmac_backlight_set_legacy_brightness(int brightness) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci int error = -ENXIO; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci mutex_lock(&pmac_backlight_mutex); 14562306a36Sopenharmony_ci if (pmac_backlight) { 14662306a36Sopenharmony_ci struct backlight_properties *props; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci props = &pmac_backlight->props; 14962306a36Sopenharmony_ci props->brightness = brightness * 15062306a36Sopenharmony_ci (props->max_brightness + 1) / 15162306a36Sopenharmony_ci (OLD_BACKLIGHT_MAX + 1); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (props->brightness > props->max_brightness) 15462306a36Sopenharmony_ci props->brightness = props->max_brightness; 15562306a36Sopenharmony_ci else if (props->brightness < 0) 15662306a36Sopenharmony_ci props->brightness = 0; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci backlight_update_status(pmac_backlight); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci error = 0; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci mutex_unlock(&pmac_backlight_mutex); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return error; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic void pmac_backlight_set_legacy_worker(struct work_struct *work) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci if (atomic_read(&kernel_backlight_disabled)) 17062306a36Sopenharmony_ci return; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci __pmac_backlight_set_legacy_brightness(pmac_backlight_set_legacy_queued); 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci/* This function is called in interrupt context */ 17662306a36Sopenharmony_civoid pmac_backlight_set_legacy_brightness_pmu(int brightness) { 17762306a36Sopenharmony_ci if (atomic_read(&kernel_backlight_disabled)) 17862306a36Sopenharmony_ci return; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci pmac_backlight_set_legacy_queued = brightness; 18162306a36Sopenharmony_ci schedule_work(&pmac_backlight_set_legacy_work); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ciint pmac_backlight_set_legacy_brightness(int brightness) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci return __pmac_backlight_set_legacy_brightness(brightness); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ciint pmac_backlight_get_legacy_brightness(void) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci int result = -ENXIO; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci mutex_lock(&pmac_backlight_mutex); 19462306a36Sopenharmony_ci if (pmac_backlight) { 19562306a36Sopenharmony_ci struct backlight_properties *props; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci props = &pmac_backlight->props; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci result = props->brightness * 20062306a36Sopenharmony_ci (OLD_BACKLIGHT_MAX + 1) / 20162306a36Sopenharmony_ci (props->max_brightness + 1); 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci mutex_unlock(&pmac_backlight_mutex); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return result; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_civoid pmac_backlight_disable(void) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci atomic_inc(&kernel_backlight_disabled); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_civoid pmac_backlight_enable(void) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci atomic_dec(&kernel_backlight_disabled); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pmac_backlight); 21962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pmac_backlight_mutex); 22062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pmac_has_backlight_type); 221