1 /* 2 * Miscellaneous procedures for dealing with the PowerMac hardware. 3 * Contains support for the backlight. 4 * 5 * Copyright (C) 2000 Benjamin Herrenschmidt 6 * Copyright (C) 2006 Michael Hanselmann <linux-kernel@hansmi.ch> 7 * 8 */ 9 10 #include <linux/kernel.h> 11 #include <linux/fb.h> 12 #include <linux/backlight.h> 13 #include <linux/adb.h> 14 #include <linux/pmu.h> 15 #include <asm/atomic.h> 16 #include <asm/prom.h> 17 #include <asm/backlight.h> 18 19 #define OLD_BACKLIGHT_MAX 15 20 21 static void pmac_backlight_key_worker(void *data); 22 static void pmac_backlight_set_legacy_worker(void *data); 23 24 static DECLARE_WORK(pmac_backlight_key_work, pmac_backlight_key_worker, NULL); 25 static DECLARE_WORK(pmac_backlight_set_legacy_work, pmac_backlight_set_legacy_worker, NULL); 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 */ 41 DEFINE_MUTEX(pmac_backlight_mutex); 42 43 /* Main backlight storage 44 * 45 * Backlight drivers in this variable are required to have the "props" 46 * attribute set and to have an update_status function. 47 * 48 * We can only store one backlight here, but since Apple laptops have only one 49 * internal display, it doesn't matter. Other backlight drivers can be used 50 * independently. 51 * 52 * Lock ordering: 53 * pmac_backlight_mutex (global, main backlight) 54 * pmac_backlight->sem (backlight class) 55 */ 56 struct backlight_device *pmac_backlight; 57 58 int pmac_has_backlight_type(const char *type) 59 { 60 struct device_node* bk_node = find_devices("backlight"); 61 62 if (bk_node) { 63 const char *prop = get_property(bk_node, 64 "backlight-control", NULL); 65 if (prop && strncmp(prop, type, strlen(type)) == 0) 66 return 1; 67 } 68 69 return 0; 70 } 71 72 int pmac_backlight_curve_lookup(struct fb_info *info, int value) 73 { 74 int level = (FB_BACKLIGHT_LEVELS - 1); 75 76 if (info && info->bl_dev) { 77 int i, max = 0; 78 79 /* Look for biggest value */ 80 for (i = 0; i < FB_BACKLIGHT_LEVELS; i++) 81 max = max((int)info->bl_curve[i], max); 82 83 /* Look for nearest value */ 84 for (i = 0; i < FB_BACKLIGHT_LEVELS; i++) { 85 int diff = abs(info->bl_curve[i] - value); 86 if (diff < max) { 87 max = diff; 88 level = i; 89 } 90 } 91 92 } 93 94 return level; 95 } 96 97 static void pmac_backlight_key_worker(void *data) 98 { 99 if (atomic_read(&kernel_backlight_disabled)) 100 return; 101 102 mutex_lock(&pmac_backlight_mutex); 103 if (pmac_backlight) { 104 struct backlight_properties *props; 105 int brightness; 106 107 down(&pmac_backlight->sem); 108 props = pmac_backlight->props; 109 110 brightness = props->brightness + 111 ((pmac_backlight_key_queued?-1:1) * 112 (props->max_brightness / 15)); 113 114 if (brightness < 0) 115 brightness = 0; 116 else if (brightness > props->max_brightness) 117 brightness = props->max_brightness; 118 119 props->brightness = brightness; 120 props->update_status(pmac_backlight); 121 122 up(&pmac_backlight->sem); 123 } 124 mutex_unlock(&pmac_backlight_mutex); 125 } 126 127 /* This function is called in interrupt context */ 128 void pmac_backlight_key(int direction) 129 { 130 if (atomic_read(&kernel_backlight_disabled)) 131 return; 132 133 /* we can receive multiple interrupts here, but the scheduled work 134 * will run only once, with the last value 135 */ 136 pmac_backlight_key_queued = direction; 137 schedule_work(&pmac_backlight_key_work); 138 } 139 140 static int __pmac_backlight_set_legacy_brightness(int brightness) 141 { 142 int error = -ENXIO; 143 144 mutex_lock(&pmac_backlight_mutex); 145 if (pmac_backlight) { 146 struct backlight_properties *props; 147 148 down(&pmac_backlight->sem); 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 props->update_status(pmac_backlight); 160 up(&pmac_backlight->sem); 161 162 error = 0; 163 } 164 mutex_unlock(&pmac_backlight_mutex); 165 166 return error; 167 } 168 169 static void pmac_backlight_set_legacy_worker(void *data) 170 { 171 if (atomic_read(&kernel_backlight_disabled)) 172 return; 173 174 __pmac_backlight_set_legacy_brightness(pmac_backlight_set_legacy_queued); 175 } 176 177 /* This function is called in interrupt context */ 178 void pmac_backlight_set_legacy_brightness_pmu(int brightness) { 179 if (atomic_read(&kernel_backlight_disabled)) 180 return; 181 182 pmac_backlight_set_legacy_queued = brightness; 183 schedule_work(&pmac_backlight_set_legacy_work); 184 } 185 186 int pmac_backlight_set_legacy_brightness(int brightness) 187 { 188 return __pmac_backlight_set_legacy_brightness(brightness); 189 } 190 191 int pmac_backlight_get_legacy_brightness() 192 { 193 int result = -ENXIO; 194 195 mutex_lock(&pmac_backlight_mutex); 196 if (pmac_backlight) { 197 struct backlight_properties *props; 198 199 down(&pmac_backlight->sem); 200 props = pmac_backlight->props; 201 202 result = props->brightness * 203 (OLD_BACKLIGHT_MAX + 1) / 204 (props->max_brightness + 1); 205 206 up(&pmac_backlight->sem); 207 } 208 mutex_unlock(&pmac_backlight_mutex); 209 210 return result; 211 } 212 213 void pmac_backlight_disable() 214 { 215 atomic_inc(&kernel_backlight_disabled); 216 } 217 218 void pmac_backlight_enable() 219 { 220 atomic_dec(&kernel_backlight_disabled); 221 } 222 223 EXPORT_SYMBOL_GPL(pmac_backlight); 224 EXPORT_SYMBOL_GPL(pmac_backlight_mutex); 225 EXPORT_SYMBOL_GPL(pmac_has_backlight_type); 226