Merge branch 'upstream-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/linvil...
[linux-2.6] / arch / powerpc / platforms / powermac / backlight.c
1 /*
2  * Miscellaneous procedures for dealing with the PowerMac hardware.
3  * Contains support for the backlight.
4  *
5  *   Copyright (C) 2000 Benjamin Herrenschmidt
6  *   Copyright (C) 2006 Michael Hanselmann <linux-kernel@hansmi.ch>
7  *
8  */
9
10 #include <linux/kernel.h>
11 #include <linux/fb.h>
12 #include <linux/backlight.h>
13 #include <asm/prom.h>
14 #include <asm/backlight.h>
15
16 #define OLD_BACKLIGHT_MAX 15
17
18 static void pmac_backlight_key_worker(void *data);
19 static DECLARE_WORK(pmac_backlight_key_work, pmac_backlight_key_worker, NULL);
20
21 /* Although this variable is used in interrupt context, it makes no sense to
22  * protect it. No user is able to produce enough key events per second and
23  * notice the errors that might happen.
24  */
25 static int pmac_backlight_key_queued;
26
27 /* Protect the pmac_backlight variable */
28 DEFINE_MUTEX(pmac_backlight_mutex);
29
30 /* Main backlight storage
31  *
32  * Backlight drivers in this variable are required to have the "props"
33  * attribute set and to have an update_status function.
34  *
35  * We can only store one backlight here, but since Apple laptops have only one
36  * internal display, it doesn't matter. Other backlight drivers can be used
37  * independently.
38  *
39  * Lock ordering:
40  * pmac_backlight_mutex (global, main backlight)
41  *   pmac_backlight->sem (backlight class)
42  */
43 struct backlight_device *pmac_backlight;
44
45 int pmac_has_backlight_type(const char *type)
46 {
47         struct device_node* bk_node = find_devices("backlight");
48
49         if (bk_node) {
50                 char *prop = get_property(bk_node, "backlight-control", NULL);
51                 if (prop && strncmp(prop, type, strlen(type)) == 0)
52                         return 1;
53         }
54
55         return 0;
56 }
57
58 int pmac_backlight_curve_lookup(struct fb_info *info, int value)
59 {
60         int level = (FB_BACKLIGHT_LEVELS - 1);
61
62         if (info && info->bl_dev) {
63                 int i, max = 0;
64
65                 /* Look for biggest value */
66                 for (i = 0; i < FB_BACKLIGHT_LEVELS; i++)
67                         max = max((int)info->bl_curve[i], max);
68
69                 /* Look for nearest value */
70                 for (i = 0; i < FB_BACKLIGHT_LEVELS; i++) {
71                         int diff = abs(info->bl_curve[i] - value);
72                         if (diff < max) {
73                                 max = diff;
74                                 level = i;
75                         }
76                 }
77
78         }
79
80         return level;
81 }
82
83 static void pmac_backlight_key_worker(void *data)
84 {
85         mutex_lock(&pmac_backlight_mutex);
86         if (pmac_backlight) {
87                 struct backlight_properties *props;
88                 int brightness;
89
90                 down(&pmac_backlight->sem);
91                 props = pmac_backlight->props;
92
93                 brightness = props->brightness +
94                         ((pmac_backlight_key_queued?-1:1) *
95                          (props->max_brightness / 15));
96
97                 if (brightness < 0)
98                         brightness = 0;
99                 else if (brightness > props->max_brightness)
100                         brightness = props->max_brightness;
101
102                 props->brightness = brightness;
103                 props->update_status(pmac_backlight);
104
105                 up(&pmac_backlight->sem);
106         }
107         mutex_unlock(&pmac_backlight_mutex);
108 }
109
110 void pmac_backlight_key(int direction)
111 {
112         /* we can receive multiple interrupts here, but the scheduled work
113          * will run only once, with the last value
114          */
115         pmac_backlight_key_queued = direction;
116         schedule_work(&pmac_backlight_key_work);
117 }
118
119 int pmac_backlight_set_legacy_brightness(int brightness)
120 {
121         int error = -ENXIO;
122
123         mutex_lock(&pmac_backlight_mutex);
124         if (pmac_backlight) {
125                 struct backlight_properties *props;
126
127                 down(&pmac_backlight->sem);
128                 props = pmac_backlight->props;
129                 props->brightness = brightness *
130                         (props->max_brightness + 1) /
131                         (OLD_BACKLIGHT_MAX + 1);
132
133                 if (props->brightness > props->max_brightness)
134                         props->brightness = props->max_brightness;
135                 else if (props->brightness < 0)
136                         props->brightness = 0;
137
138                 props->update_status(pmac_backlight);
139                 up(&pmac_backlight->sem);
140
141                 error = 0;
142         }
143         mutex_unlock(&pmac_backlight_mutex);
144
145         return error;
146 }
147
148 int pmac_backlight_get_legacy_brightness()
149 {
150         int result = -ENXIO;
151
152         mutex_lock(&pmac_backlight_mutex);
153         if (pmac_backlight) {
154                 struct backlight_properties *props;
155
156                 down(&pmac_backlight->sem);
157                 props = pmac_backlight->props;
158
159                 result = props->brightness *
160                         (OLD_BACKLIGHT_MAX + 1) /
161                         (props->max_brightness + 1);
162
163                 up(&pmac_backlight->sem);
164         }
165         mutex_unlock(&pmac_backlight_mutex);
166
167         return result;
168 }
169
170 EXPORT_SYMBOL_GPL(pmac_backlight);
171 EXPORT_SYMBOL_GPL(pmac_backlight_mutex);
172 EXPORT_SYMBOL_GPL(pmac_has_backlight_type);