1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Miscellaneous procedures for dealing with the PowerMac hardware. 4 * Contains support for the backlight. 5 * 6 * Copyright (C) 2000 Benjamin Herrenschmidt 7 * Copyright (C) 2006 Michael Hanselmann <linux-kernel@hansmi.ch> 8 * 9 */ 10 11#include <linux/kernel.h> 12#include <linux/fb.h> 13#include <linux/backlight.h> 14#include <linux/adb.h> 15#include <linux/pmu.h> 16#include <linux/atomic.h> 17#include <linux/export.h> 18#include <asm/prom.h> 19#include <asm/backlight.h> 20 21#define OLD_BACKLIGHT_MAX 15 22 23static void pmac_backlight_key_worker(struct work_struct *work); 24static void pmac_backlight_set_legacy_worker(struct work_struct *work); 25 26static DECLARE_WORK(pmac_backlight_key_work, pmac_backlight_key_worker); 27static DECLARE_WORK(pmac_backlight_set_legacy_work, pmac_backlight_set_legacy_worker); 28 29/* Although these variables are used in interrupt context, it makes no sense to 30 * protect them. No user is able to produce enough key events per second and 31 * notice the errors that might happen. 32 */ 33static int pmac_backlight_key_queued; 34static int pmac_backlight_set_legacy_queued; 35 36/* The via-pmu code allows the backlight to be grabbed, in which case the 37 * in-kernel control of the brightness needs to be disabled. This should 38 * only be used by really old PowerBooks. 39 */ 40static atomic_t kernel_backlight_disabled = ATOMIC_INIT(0); 41 42/* Protect the pmac_backlight variable below. 43 You should hold this lock when using the pmac_backlight pointer to 44 prevent its potential removal. */ 45DEFINE_MUTEX(pmac_backlight_mutex); 46 47/* Main backlight storage 48 * 49 * Backlight drivers in this variable are required to have the "ops" 50 * attribute set and to have an update_status function. 51 * 52 * We can only store one backlight here, but since Apple laptops have only one 53 * internal display, it doesn't matter. Other backlight drivers can be used 54 * independently. 55 * 56 */ 57struct backlight_device *pmac_backlight; 58 59int pmac_has_backlight_type(const char *type) 60{ 61 struct device_node* bk_node = of_find_node_by_name(NULL, "backlight"); 62 63 if (bk_node) { 64 const char *prop = of_get_property(bk_node, 65 "backlight-control", NULL); 66 if (prop && strncmp(prop, type, strlen(type)) == 0) { 67 of_node_put(bk_node); 68 return 1; 69 } 70 of_node_put(bk_node); 71 } 72 73 return 0; 74} 75 76int pmac_backlight_curve_lookup(struct fb_info *info, int value) 77{ 78 int level = (FB_BACKLIGHT_LEVELS - 1); 79 80 if (info && info->bl_dev) { 81 int i, max = 0; 82 83 /* Look for biggest value */ 84 for (i = 0; i < FB_BACKLIGHT_LEVELS; i++) 85 max = max((int)info->bl_curve[i], max); 86 87 /* Look for nearest value */ 88 for (i = 0; i < FB_BACKLIGHT_LEVELS; i++) { 89 int diff = abs(info->bl_curve[i] - value); 90 if (diff < max) { 91 max = diff; 92 level = i; 93 } 94 } 95 96 } 97 98 return level; 99} 100 101static void pmac_backlight_key_worker(struct work_struct *work) 102{ 103 if (atomic_read(&kernel_backlight_disabled)) 104 return; 105 106 mutex_lock(&pmac_backlight_mutex); 107 if (pmac_backlight) { 108 struct backlight_properties *props; 109 int brightness; 110 111 props = &pmac_backlight->props; 112 113 brightness = props->brightness + 114 ((pmac_backlight_key_queued?-1:1) * 115 (props->max_brightness / 15)); 116 117 if (brightness < 0) 118 brightness = 0; 119 else if (brightness > props->max_brightness) 120 brightness = props->max_brightness; 121 122 props->brightness = brightness; 123 backlight_update_status(pmac_backlight); 124 } 125 mutex_unlock(&pmac_backlight_mutex); 126} 127 128/* This function is called in interrupt context */ 129void pmac_backlight_key(int direction) 130{ 131 if (atomic_read(&kernel_backlight_disabled)) 132 return; 133 134 /* we can receive multiple interrupts here, but the scheduled work 135 * will run only once, with the last value 136 */ 137 pmac_backlight_key_queued = direction; 138 schedule_work(&pmac_backlight_key_work); 139} 140 141static int __pmac_backlight_set_legacy_brightness(int brightness) 142{ 143 int error = -ENXIO; 144 145 mutex_lock(&pmac_backlight_mutex); 146 if (pmac_backlight) { 147 struct backlight_properties *props; 148 149 props = &pmac_backlight->props; 150 props->brightness = brightness * 151 (props->max_brightness + 1) / 152 (OLD_BACKLIGHT_MAX + 1); 153 154 if (props->brightness > props->max_brightness) 155 props->brightness = props->max_brightness; 156 else if (props->brightness < 0) 157 props->brightness = 0; 158 159 backlight_update_status(pmac_backlight); 160 161 error = 0; 162 } 163 mutex_unlock(&pmac_backlight_mutex); 164 165 return error; 166} 167 168static void pmac_backlight_set_legacy_worker(struct work_struct *work) 169{ 170 if (atomic_read(&kernel_backlight_disabled)) 171 return; 172 173 __pmac_backlight_set_legacy_brightness(pmac_backlight_set_legacy_queued); 174} 175 176/* This function is called in interrupt context */ 177void pmac_backlight_set_legacy_brightness_pmu(int brightness) { 178 if (atomic_read(&kernel_backlight_disabled)) 179 return; 180 181 pmac_backlight_set_legacy_queued = brightness; 182 schedule_work(&pmac_backlight_set_legacy_work); 183} 184 185int pmac_backlight_set_legacy_brightness(int brightness) 186{ 187 return __pmac_backlight_set_legacy_brightness(brightness); 188} 189 190int pmac_backlight_get_legacy_brightness(void) 191{ 192 int result = -ENXIO; 193 194 mutex_lock(&pmac_backlight_mutex); 195 if (pmac_backlight) { 196 struct backlight_properties *props; 197 198 props = &pmac_backlight->props; 199 200 result = props->brightness * 201 (OLD_BACKLIGHT_MAX + 1) / 202 (props->max_brightness + 1); 203 } 204 mutex_unlock(&pmac_backlight_mutex); 205 206 return result; 207} 208 209void pmac_backlight_disable(void) 210{ 211 atomic_inc(&kernel_backlight_disabled); 212} 213 214void pmac_backlight_enable(void) 215{ 216 atomic_dec(&kernel_backlight_disabled); 217} 218 219EXPORT_SYMBOL_GPL(pmac_backlight); 220EXPORT_SYMBOL_GPL(pmac_backlight_mutex); 221EXPORT_SYMBOL_GPL(pmac_has_backlight_type); 222