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