diff options
author | Michael Hanselmann <linux-kernel@hansmi.ch> | 2006-06-25 08:47:08 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-06-25 13:00:59 -0400 |
commit | 5474c120aafe78ca54bf272f7a01107c42da2b21 (patch) | |
tree | c1b002a27703ce92c816bfb9844752186e33d403 /arch/powerpc/platforms | |
parent | 17660bdd5c1f1a165273c1a59cb5b87670a81cc4 (diff) |
[PATCH] Rewritten backlight infrastructure for portable Apple computers
This patch contains a total rewrite of the backlight infrastructure for
portable Apple computers. Backward compatibility is retained. A sysfs
interface allows userland to control the brightness with more steps than
before. Userland is allowed to upload a brightness curve for different
monitors, similar to Mac OS X.
[akpm@osdl.org: add needed exports]
Signed-off-by: Michael Hanselmann <linux-kernel@hansmi.ch>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Richard Purdie <rpurdie@rpsys.net>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/powerpc/platforms')
-rw-r--r-- | arch/powerpc/platforms/powermac/backlight.c | 270 |
1 files changed, 109 insertions, 161 deletions
diff --git a/arch/powerpc/platforms/powermac/backlight.c b/arch/powerpc/platforms/powermac/backlight.c index 8be2f7d071f0..498b042e1837 100644 --- a/arch/powerpc/platforms/powermac/backlight.c +++ b/arch/powerpc/platforms/powermac/backlight.c | |||
@@ -3,200 +3,148 @@ | |||
3 | * Contains support for the backlight. | 3 | * Contains support for the backlight. |
4 | * | 4 | * |
5 | * Copyright (C) 2000 Benjamin Herrenschmidt | 5 | * Copyright (C) 2000 Benjamin Herrenschmidt |
6 | * Copyright (C) 2006 Michael Hanselmann <linux-kernel@hansmi.ch> | ||
6 | * | 7 | * |
7 | */ | 8 | */ |
8 | 9 | ||
9 | #include <linux/config.h> | 10 | #include <linux/config.h> |
10 | #include <linux/kernel.h> | 11 | #include <linux/kernel.h> |
11 | #include <linux/module.h> | 12 | #include <linux/fb.h> |
12 | #include <linux/stddef.h> | 13 | #include <linux/backlight.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> | 14 | #include <asm/prom.h> |
22 | #include <asm/machdep.h> | ||
23 | #include <asm/nvram.h> | ||
24 | #include <asm/backlight.h> | 15 | #include <asm/backlight.h> |
25 | 16 | ||
26 | #include <linux/adb.h> | 17 | #define OLD_BACKLIGHT_MAX 15 |
27 | #include <linux/pmu.h> | ||
28 | 18 | ||
29 | static struct backlight_controller *backlighter; | 19 | /* Protect the pmac_backlight variable */ |
30 | static void* backlighter_data; | 20 | DEFINE_MUTEX(pmac_backlight_mutex); |
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 | 21 | ||
37 | static void backlight_callback(void *); | 22 | /* Main backlight storage |
38 | static DECLARE_WORK(backlight_work, backlight_callback, NULL); | 23 | * |
24 | * Backlight drivers in this variable are required to have the "props" | ||
25 | * attribute set and to have an update_status function. | ||
26 | * | ||
27 | * We can only store one backlight here, but since Apple laptops have only one | ||
28 | * internal display, it doesn't matter. Other backlight drivers can be used | ||
29 | * independently. | ||
30 | * | ||
31 | * Lock ordering: | ||
32 | * pmac_backlight_mutex (global, main backlight) | ||
33 | * pmac_backlight->sem (backlight class) | ||
34 | */ | ||
35 | struct backlight_device *pmac_backlight; | ||
39 | 36 | ||
40 | void register_backlight_controller(struct backlight_controller *ctrler, | 37 | int pmac_has_backlight_type(const char *type) |
41 | void *data, char *type) | ||
42 | { | 38 | { |
43 | struct device_node* bk_node; | 39 | struct device_node* bk_node = find_devices("backlight"); |
44 | char *prop; | 40 | |
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) { | 41 | if (bk_node) { |
64 | prop = get_property(bk_node, "backlight-control", NULL); | 42 | char *prop = get_property(bk_node, "backlight-control", NULL); |
65 | if (prop && !strncmp(prop, type, strlen(type))) | 43 | if (prop && strncmp(prop, type, strlen(type)) == 0) |
66 | valid = 1; | 44 | return 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 | } | 45 | } |
82 | 46 | ||
83 | #ifdef CONFIG_ADB_PMU | 47 | return 0; |
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 | } | 48 | } |
100 | EXPORT_SYMBOL(register_backlight_controller); | ||
101 | 49 | ||
102 | void unregister_backlight_controller(struct backlight_controller | 50 | int pmac_backlight_curve_lookup(struct fb_info *info, int value) |
103 | *ctrler, void *data) | ||
104 | { | 51 | { |
105 | /* We keep the current backlight level (for now) */ | 52 | int level = (FB_BACKLIGHT_LEVELS - 1); |
106 | if (ctrler == backlighter && data == backlighter_data) | 53 | |
107 | backlighter = NULL; | 54 | if (info && info->bl_dev) { |
55 | int i, max = 0; | ||
56 | |||
57 | /* Look for biggest value */ | ||
58 | for (i = 0; i < FB_BACKLIGHT_LEVELS; i++) | ||
59 | max = max((int)info->bl_curve[i], max); | ||
60 | |||
61 | /* Look for nearest value */ | ||
62 | for (i = 0; i < FB_BACKLIGHT_LEVELS; i++) { | ||
63 | int diff = abs(info->bl_curve[i] - value); | ||
64 | if (diff < max) { | ||
65 | max = diff; | ||
66 | level = i; | ||
67 | } | ||
68 | } | ||
69 | |||
70 | } | ||
71 | |||
72 | return level; | ||
108 | } | 73 | } |
109 | EXPORT_SYMBOL(unregister_backlight_controller); | ||
110 | 74 | ||
111 | static int __set_backlight_enable(int enable) | 75 | static void pmac_backlight_key(int direction) |
112 | { | 76 | { |
113 | int rc; | 77 | mutex_lock(&pmac_backlight_mutex); |
114 | 78 | if (pmac_backlight) { | |
115 | if (!backlighter) | 79 | struct backlight_properties *props; |
116 | return -ENODEV; | 80 | int brightness; |
117 | acquire_console_sem(); | 81 | |
118 | rc = backlighter->set_enable(enable, backlight_level, | 82 | down(&pmac_backlight->sem); |
119 | backlighter_data); | 83 | props = pmac_backlight->props; |
120 | if (!rc) | 84 | |
121 | backlight_enabled = enable; | 85 | brightness = props->brightness + |
122 | release_console_sem(); | 86 | ((direction?-1:1) * (props->max_brightness / 15)); |
123 | return rc; | 87 | |
88 | if (brightness < 0) | ||
89 | brightness = 0; | ||
90 | else if (brightness > props->max_brightness) | ||
91 | brightness = props->max_brightness; | ||
92 | |||
93 | props->brightness = brightness; | ||
94 | props->update_status(pmac_backlight); | ||
95 | |||
96 | up(&pmac_backlight->sem); | ||
97 | } | ||
98 | mutex_unlock(&pmac_backlight_mutex); | ||
124 | } | 99 | } |
125 | int set_backlight_enable(int enable) | 100 | |
101 | void pmac_backlight_key_up() | ||
126 | { | 102 | { |
127 | if (!backlighter) | 103 | pmac_backlight_key(0); |
128 | return -ENODEV; | ||
129 | backlight_req_enable = enable; | ||
130 | schedule_work(&backlight_work); | ||
131 | return 0; | ||
132 | } | 104 | } |
133 | 105 | ||
134 | EXPORT_SYMBOL(set_backlight_enable); | 106 | void pmac_backlight_key_down() |
135 | |||
136 | int get_backlight_enable(void) | ||
137 | { | 107 | { |
138 | if (!backlighter) | 108 | pmac_backlight_key(1); |
139 | return -ENODEV; | ||
140 | return backlight_enabled; | ||
141 | } | 109 | } |
142 | EXPORT_SYMBOL(get_backlight_enable); | ||
143 | 110 | ||
144 | static int __set_backlight_level(int level) | 111 | int pmac_backlight_set_legacy_brightness(int brightness) |
145 | { | 112 | { |
146 | int rc = 0; | 113 | int error = -ENXIO; |
147 | 114 | ||
148 | if (!backlighter) | 115 | mutex_lock(&pmac_backlight_mutex); |
149 | return -ENODEV; | 116 | if (pmac_backlight) { |
150 | if (level < BACKLIGHT_MIN) | 117 | struct backlight_properties *props; |
151 | level = BACKLIGHT_OFF; | 118 | |
152 | if (level > BACKLIGHT_MAX) | 119 | down(&pmac_backlight->sem); |
153 | level = BACKLIGHT_MAX; | 120 | props = pmac_backlight->props; |
154 | acquire_console_sem(); | 121 | props->brightness = brightness * |
155 | if (backlight_enabled) | 122 | props->max_brightness / OLD_BACKLIGHT_MAX; |
156 | rc = backlighter->set_level(level, backlighter_data); | 123 | props->update_status(pmac_backlight); |
157 | if (!rc) | 124 | up(&pmac_backlight->sem); |
158 | backlight_level = level; | 125 | |
159 | release_console_sem(); | 126 | error = 0; |
160 | if (!rc && !backlight_autosave) { | ||
161 | level <<=1; | ||
162 | if (level & 0x10) | ||
163 | level |= 0x01; | ||
164 | // -- todo: save to property "bklt" | ||
165 | } | 127 | } |
166 | return rc; | 128 | mutex_unlock(&pmac_backlight_mutex); |
129 | |||
130 | return error; | ||
167 | } | 131 | } |
168 | int set_backlight_level(int level) | 132 | |
133 | int pmac_backlight_get_legacy_brightness() | ||
169 | { | 134 | { |
170 | if (!backlighter) | 135 | int result = -ENXIO; |
171 | return -ENODEV; | ||
172 | backlight_req_level = level; | ||
173 | schedule_work(&backlight_work); | ||
174 | return 0; | ||
175 | } | ||
176 | 136 | ||
177 | EXPORT_SYMBOL(set_backlight_level); | 137 | mutex_lock(&pmac_backlight_mutex); |
138 | if (pmac_backlight) { | ||
139 | struct backlight_properties *props; | ||
178 | 140 | ||
179 | int get_backlight_level(void) | 141 | down(&pmac_backlight->sem); |
180 | { | 142 | props = pmac_backlight->props; |
181 | if (!backlighter) | 143 | result = props->brightness * |
182 | return -ENODEV; | 144 | OLD_BACKLIGHT_MAX / props->max_brightness; |
183 | return backlight_level; | 145 | up(&pmac_backlight->sem); |
184 | } | 146 | } |
185 | EXPORT_SYMBOL(get_backlight_level); | 147 | mutex_unlock(&pmac_backlight_mutex); |
186 | 148 | ||
187 | static void backlight_callback(void *dummy) | 149 | return result; |
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 | } | 150 | } |