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