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 23 static void pmac_backlight_key_worker(struct work_struct *work); 24 static void pmac_backlight_set_legacy_worker(struct work_struct *work); 25 26 static DECLARE_WORK(pmac_backlight_key_work, pmac_backlight_key_worker); 27 static 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 */ 33 static int pmac_backlight_key_queued; 34 static 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 */ 40 static 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. */ 45 DEFINE_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 */ 57 struct backlight_device *pmac_backlight; 58 59 int 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 76 int 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 101 static 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 */ 129 void 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 141 static 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 168 static 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 */ 177 void 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 185 int pmac_backlight_set_legacy_brightness(int brightness) 186 { 187 return __pmac_backlight_set_legacy_brightness(brightness); 188 } 189 190 int 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 209 void pmac_backlight_disable(void) 210 { 211 atomic_inc(&kernel_backlight_disabled); 212 } 213 214 void pmac_backlight_enable(void) 215 { 216 atomic_dec(&kernel_backlight_disabled); 217 } 218 219 EXPORT_SYMBOL_GPL(pmac_backlight); 220 EXPORT_SYMBOL_GPL(pmac_backlight_mutex); 221 EXPORT_SYMBOL_GPL(pmac_has_backlight_type); 222