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(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 = find_devices("backlight"); 60 61 if (bk_node) { 62 const char *prop = get_property(bk_node, 63 "backlight-control", NULL); 64 if (prop && strncmp(prop, type, strlen(type)) == 0) 65 return 1; 66 } 67 68 return 0; 69 } 70 71 int pmac_backlight_curve_lookup(struct fb_info *info, int value) 72 { 73 int level = (FB_BACKLIGHT_LEVELS - 1); 74 75 if (info && info->bl_dev) { 76 int i, max = 0; 77 78 /* Look for biggest value */ 79 for (i = 0; i < FB_BACKLIGHT_LEVELS; i++) 80 max = max((int)info->bl_curve[i], max); 81 82 /* Look for nearest value */ 83 for (i = 0; i < FB_BACKLIGHT_LEVELS; i++) { 84 int diff = abs(info->bl_curve[i] - value); 85 if (diff < max) { 86 max = diff; 87 level = i; 88 } 89 } 90 91 } 92 93 return level; 94 } 95 96 static void pmac_backlight_key_worker(struct work_struct *work) 97 { 98 if (atomic_read(&kernel_backlight_disabled)) 99 return; 100 101 mutex_lock(&pmac_backlight_mutex); 102 if (pmac_backlight) { 103 struct backlight_properties *props; 104 int brightness; 105 106 props = &pmac_backlight->props; 107 108 brightness = props->brightness + 109 ((pmac_backlight_key_queued?-1:1) * 110 (props->max_brightness / 15)); 111 112 if (brightness < 0) 113 brightness = 0; 114 else if (brightness > props->max_brightness) 115 brightness = props->max_brightness; 116 117 props->brightness = brightness; 118 backlight_update_status(pmac_backlight); 119 } 120 mutex_unlock(&pmac_backlight_mutex); 121 } 122 123 /* This function is called in interrupt context */ 124 void pmac_backlight_key(int direction) 125 { 126 if (atomic_read(&kernel_backlight_disabled)) 127 return; 128 129 /* we can receive multiple interrupts here, but the scheduled work 130 * will run only once, with the last value 131 */ 132 pmac_backlight_key_queued = direction; 133 schedule_work(&pmac_backlight_key_work); 134 } 135 136 static int __pmac_backlight_set_legacy_brightness(int brightness) 137 { 138 int error = -ENXIO; 139 140 mutex_lock(&pmac_backlight_mutex); 141 if (pmac_backlight) { 142 struct backlight_properties *props; 143 144 props = &pmac_backlight->props; 145 props->brightness = brightness * 146 (props->max_brightness + 1) / 147 (OLD_BACKLIGHT_MAX + 1); 148 149 if (props->brightness > props->max_brightness) 150 props->brightness = props->max_brightness; 151 else if (props->brightness < 0) 152 props->brightness = 0; 153 154 backlight_update_status(pmac_backlight); 155 156 error = 0; 157 } 158 mutex_unlock(&pmac_backlight_mutex); 159 160 return error; 161 } 162 163 static void pmac_backlight_set_legacy_worker(struct work_struct *work) 164 { 165 if (atomic_read(&kernel_backlight_disabled)) 166 return; 167 168 __pmac_backlight_set_legacy_brightness(pmac_backlight_set_legacy_queued); 169 } 170 171 /* This function is called in interrupt context */ 172 void pmac_backlight_set_legacy_brightness_pmu(int brightness) { 173 if (atomic_read(&kernel_backlight_disabled)) 174 return; 175 176 pmac_backlight_set_legacy_queued = brightness; 177 schedule_work(&pmac_backlight_set_legacy_work); 178 } 179 180 int pmac_backlight_set_legacy_brightness(int brightness) 181 { 182 return __pmac_backlight_set_legacy_brightness(brightness); 183 } 184 185 int pmac_backlight_get_legacy_brightness() 186 { 187 int result = -ENXIO; 188 189 mutex_lock(&pmac_backlight_mutex); 190 if (pmac_backlight) { 191 struct backlight_properties *props; 192 193 props = &pmac_backlight->props; 194 195 result = props->brightness * 196 (OLD_BACKLIGHT_MAX + 1) / 197 (props->max_brightness + 1); 198 } 199 mutex_unlock(&pmac_backlight_mutex); 200 201 return result; 202 } 203 204 void pmac_backlight_disable() 205 { 206 atomic_inc(&kernel_backlight_disabled); 207 } 208 209 void pmac_backlight_enable() 210 { 211 atomic_dec(&kernel_backlight_disabled); 212 } 213 214 EXPORT_SYMBOL_GPL(pmac_backlight); 215 EXPORT_SYMBOL_GPL(pmac_backlight_mutex); 216 EXPORT_SYMBOL_GPL(pmac_has_backlight_type); 217