blob: ed2b1cebc19ae3ec28dc5ce11b010c6d861add81 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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
29static struct backlight_controller *backlighter;
30static void* backlighter_data;
31static int backlight_autosave;
32static int backlight_level = BACKLIGHT_MAX;
33static int backlight_enabled = 1;
34static int backlight_req_level = -1;
35static int backlight_req_enable = -1;
36
37static void backlight_callback(void *);
38static DECLARE_WORK(backlight_work, backlight_callback, NULL);
39
40void __pmac 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}
100EXPORT_SYMBOL(register_backlight_controller);
101
102void __pmac 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}
109EXPORT_SYMBOL(unregister_backlight_controller);
110
111static int __pmac __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}
125int __pmac 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
134EXPORT_SYMBOL(set_backlight_enable);
135
136int __pmac get_backlight_enable(void)
137{
138 if (!backlighter)
139 return -ENODEV;
140 return backlight_enabled;
141}
142EXPORT_SYMBOL(get_backlight_enable);
143
144static int __pmac __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}
168int __pmac 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
177EXPORT_SYMBOL(set_backlight_level);
178
179int __pmac get_backlight_level(void)
180{
181 if (!backlighter)
182 return -ENODEV;
183 return backlight_level;
184}
185EXPORT_SYMBOL(get_backlight_level);
186
187static 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}