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