1 /* 2 * Miscellaneous procedures for dealing with the PowerMac hardware. 3 * Contains support for the backlight. 4 * 5 * Copyright (C) 2000 Benjamin Herrenschmidt 6 * 7 */ 8 9 #include <linux/config.h> 10 #include <linux/kernel.h> 11 #include <linux/module.h> 12 #include <linux/stddef.h> 13 #include <linux/reboot.h> 14 #include <linux/nvram.h> 15 #include <linux/console.h> 16 #include <asm/sections.h> 17 #include <asm/ptrace.h> 18 #include <asm/io.h> 19 #include <asm/pgtable.h> 20 #include <asm/system.h> 21 #include <asm/prom.h> 22 #include <asm/machdep.h> 23 #include <asm/nvram.h> 24 #include <asm/backlight.h> 25 26 #include <linux/adb.h> 27 #include <linux/pmu.h> 28 29 static struct backlight_controller *backlighter; 30 static void* backlighter_data; 31 static int backlight_autosave; 32 static int backlight_level = BACKLIGHT_MAX; 33 static int backlight_enabled = 1; 34 static int backlight_req_level = -1; 35 static int backlight_req_enable = -1; 36 37 static void backlight_callback(void *); 38 static DECLARE_WORK(backlight_work, backlight_callback, NULL); 39 40 void register_backlight_controller(struct backlight_controller *ctrler, 41 void *data, char *type) 42 { 43 struct device_node* bk_node; 44 char *prop; 45 int valid = 0; 46 47 /* There's already a matching controller, bail out */ 48 if (backlighter != NULL) 49 return; 50 51 bk_node = find_devices("backlight"); 52 53 #ifdef CONFIG_ADB_PMU 54 /* Special case for the old PowerBook since I can't test on it */ 55 backlight_autosave = machine_is_compatible("AAPL,3400/2400") 56 || machine_is_compatible("AAPL,3500"); 57 if ((backlight_autosave 58 || machine_is_compatible("AAPL,PowerBook1998") 59 || machine_is_compatible("PowerBook1,1")) 60 && !strcmp(type, "pmu")) 61 valid = 1; 62 #endif 63 if (bk_node) { 64 prop = get_property(bk_node, "backlight-control", NULL); 65 if (prop && !strncmp(prop, type, strlen(type))) 66 valid = 1; 67 } 68 if (!valid) 69 return; 70 backlighter = ctrler; 71 backlighter_data = data; 72 73 if (bk_node && !backlight_autosave) 74 prop = get_property(bk_node, "bklt", NULL); 75 else 76 prop = NULL; 77 if (prop) { 78 backlight_level = ((*prop)+1) >> 1; 79 if (backlight_level > BACKLIGHT_MAX) 80 backlight_level = BACKLIGHT_MAX; 81 } 82 83 #ifdef CONFIG_ADB_PMU 84 if (backlight_autosave) { 85 struct adb_request req; 86 pmu_request(&req, NULL, 2, 0xd9, 0); 87 while (!req.complete) 88 pmu_poll(); 89 backlight_level = req.reply[0] >> 4; 90 } 91 #endif 92 acquire_console_sem(); 93 if (!backlighter->set_enable(1, backlight_level, data)) 94 backlight_enabled = 1; 95 release_console_sem(); 96 97 printk(KERN_INFO "Registered \"%s\" backlight controller," 98 "level: %d/15\n", type, backlight_level); 99 } 100 EXPORT_SYMBOL(register_backlight_controller); 101 102 void unregister_backlight_controller(struct backlight_controller 103 *ctrler, void *data) 104 { 105 /* We keep the current backlight level (for now) */ 106 if (ctrler == backlighter && data == backlighter_data) 107 backlighter = NULL; 108 } 109 EXPORT_SYMBOL(unregister_backlight_controller); 110 111 static int __set_backlight_enable(int enable) 112 { 113 int rc; 114 115 if (!backlighter) 116 return -ENODEV; 117 acquire_console_sem(); 118 rc = backlighter->set_enable(enable, backlight_level, 119 backlighter_data); 120 if (!rc) 121 backlight_enabled = enable; 122 release_console_sem(); 123 return rc; 124 } 125 int set_backlight_enable(int enable) 126 { 127 if (!backlighter) 128 return -ENODEV; 129 backlight_req_enable = enable; 130 schedule_work(&backlight_work); 131 return 0; 132 } 133 134 EXPORT_SYMBOL(set_backlight_enable); 135 136 int get_backlight_enable(void) 137 { 138 if (!backlighter) 139 return -ENODEV; 140 return backlight_enabled; 141 } 142 EXPORT_SYMBOL(get_backlight_enable); 143 144 static int __set_backlight_level(int level) 145 { 146 int rc = 0; 147 148 if (!backlighter) 149 return -ENODEV; 150 if (level < BACKLIGHT_MIN) 151 level = BACKLIGHT_OFF; 152 if (level > BACKLIGHT_MAX) 153 level = BACKLIGHT_MAX; 154 acquire_console_sem(); 155 if (backlight_enabled) 156 rc = backlighter->set_level(level, backlighter_data); 157 if (!rc) 158 backlight_level = level; 159 release_console_sem(); 160 if (!rc && !backlight_autosave) { 161 level <<=1; 162 if (level & 0x10) 163 level |= 0x01; 164 // -- todo: save to property "bklt" 165 } 166 return rc; 167 } 168 int set_backlight_level(int level) 169 { 170 if (!backlighter) 171 return -ENODEV; 172 backlight_req_level = level; 173 schedule_work(&backlight_work); 174 return 0; 175 } 176 177 EXPORT_SYMBOL(set_backlight_level); 178 179 int get_backlight_level(void) 180 { 181 if (!backlighter) 182 return -ENODEV; 183 return backlight_level; 184 } 185 EXPORT_SYMBOL(get_backlight_level); 186 187 static void backlight_callback(void *dummy) 188 { 189 int level, enable; 190 191 do { 192 level = backlight_req_level; 193 enable = backlight_req_enable; 194 mb(); 195 196 if (level >= 0) 197 __set_backlight_level(level); 198 if (enable >= 0) 199 __set_backlight_enable(enable); 200 } while(cmpxchg(&backlight_req_level, level, -1) != level || 201 cmpxchg(&backlight_req_enable, enable, -1) != enable); 202 } 203