Commit | Line | Data |
---|---|---|
14cf11af PM |
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 | } |