]> git.karo-electronics.de Git - mv-sheeva.git/blob - drivers/misc/compal-laptop.c
misc,acpi,backlight: compal Laptop Extras
[mv-sheeva.git] / drivers / misc / compal-laptop.c
1 /*-*-linux-c-*-*/
2
3 /*
4   Copyright (C) 2008 Cezary Jackiewicz <cezary.jackiewicz (at) gmail.com>
5
6   based on MSI driver
7
8   Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
9
10   This program is free software; you can redistribute it and/or modify
11   it under the terms of the GNU General Public License as published by
12   the Free Software Foundation; either version 2 of the License, or
13   (at your option) any later version.
14
15   This program is distributed in the hope that it will be useful, but
16   WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18   General Public License for more details.
19
20   You should have received a copy of the GNU General Public License
21   along with this program; if not, write to the Free Software
22   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23   02110-1301, USA.
24  */
25
26 /*
27  * comapl-laptop.c - Compal laptop support.
28  *
29  * This driver exports a few files in /sys/devices/platform/compal-laptop/:
30  *
31  *   lcd_level - Screen brightness: contains a single integer in the
32  *   range 0..7. (rw)
33  *
34  *   wlan - wlan subsystem state: contains 0 or 1 (rw)
35  *
36  *   bluetooth - Bluetooth subsystem state: contains 0 or 1 (rw)
37  *
38  *   raw - raw value taken from embedded controller register (ro)
39  *
40  * In addition to these platform device attributes the driver
41  * registers itself in the Linux backlight control subsystem and is
42  * available to userspace under /sys/class/backlight/compal-laptop/.
43  *
44  * This driver might work on other laptops produced by Compal. If you
45  * want to try it you can pass force=1 as argument to the module which
46  * will force it to load even when the DMI data doesn't identify the
47  * laptop as IFL90.
48  */
49
50 #include <linux/module.h>
51 #include <linux/kernel.h>
52 #include <linux/init.h>
53 #include <linux/acpi.h>
54 #include <linux/dmi.h>
55 #include <linux/backlight.h>
56 #include <linux/platform_device.h>
57 #include <linux/autoconf.h>
58
59 #define COMPAL_DRIVER_VERSION "0.2.5"
60
61 #define COMPAL_LCD_LEVEL_MAX 8
62
63 #define COMPAL_EC_COMMAND_WIRELESS 0xBB
64 #define COMPAL_EC_COMMAND_LCD_LEVEL 0xB9
65
66 #define KILLSWITCH_MASK 0x10
67 #define WLAN_MASK       0x01
68 #define BT_MASK         0x02
69
70 static int force;
71 module_param(force, bool, 0);
72 MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
73
74 /* Hardware access */
75
76 static int set_lcd_level(int level)
77 {
78         if (level < 0 || level >= COMPAL_LCD_LEVEL_MAX)
79                 return -EINVAL;
80
81         ec_write(COMPAL_EC_COMMAND_LCD_LEVEL, level);
82
83         return 0;
84 }
85
86 static int get_lcd_level(void)
87 {
88         u8 result;
89
90         ec_read(COMPAL_EC_COMMAND_LCD_LEVEL, &result);
91
92         return (int) result;
93 }
94
95 static int set_wlan_state(int state)
96 {
97         u8 result, value;
98
99         ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
100
101         if ((result & KILLSWITCH_MASK) == 0)
102                 return -EINVAL;
103         else {
104                 if (state)
105                         value = (u8) (result | WLAN_MASK);
106                 else
107                         value = (u8) (result & ~WLAN_MASK);
108                 ec_write(COMPAL_EC_COMMAND_WIRELESS, value);
109         }
110
111         return 0;
112 }
113
114 static int set_bluetooth_state(int state)
115 {
116         u8 result, value;
117
118         ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
119
120         if ((result & KILLSWITCH_MASK) == 0)
121                 return -EINVAL;
122         else {
123                 if (state)
124                         value = (u8) (result | BT_MASK);
125                 else
126                         value = (u8) (result & ~BT_MASK);
127                 ec_write(COMPAL_EC_COMMAND_WIRELESS, value);
128         }
129
130         return 0;
131 }
132
133 static int get_wireless_state(int *wlan, int *bluetooth)
134 {
135         u8 result;
136
137         ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
138
139         if (wlan) {
140                 if ((result & KILLSWITCH_MASK) == 0)
141                         *wlan = 0;
142                 else
143                         *wlan = result & WLAN_MASK;
144         }
145
146         if (bluetooth) {
147                 if ((result & KILLSWITCH_MASK) == 0)
148                         *bluetooth = 0;
149                 else
150                         *bluetooth = (result & BT_MASK) >> 1;
151         }
152
153         return 0;
154 }
155
156 /* Backlight device stuff */
157
158 static int bl_get_brightness(struct backlight_device *b)
159 {
160         return get_lcd_level();
161 }
162
163
164 static int bl_update_status(struct backlight_device *b)
165 {
166         return set_lcd_level(b->props.brightness);
167 }
168
169 static struct backlight_ops compalbl_ops = {
170         .get_brightness = bl_get_brightness,
171         .update_status  = bl_update_status,
172 };
173
174 static struct backlight_device *compalbl_device;
175
176 /* Platform device */
177
178 static ssize_t show_wlan(struct device *dev,
179         struct device_attribute *attr, char *buf)
180 {
181         int ret, enabled;
182
183         ret = get_wireless_state(&enabled, NULL);
184         if (ret < 0)
185                 return ret;
186
187         return sprintf(buf, "%i\n", enabled);
188 }
189
190 static ssize_t show_raw(struct device *dev,
191         struct device_attribute *attr, char *buf)
192 {
193         u8 result;
194
195         ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
196
197         return sprintf(buf, "%i\n", result);
198 }
199
200 static ssize_t show_bluetooth(struct device *dev,
201         struct device_attribute *attr, char *buf)
202 {
203         int ret, enabled;
204
205         ret = get_wireless_state(NULL, &enabled);
206         if (ret < 0)
207                 return ret;
208
209         return sprintf(buf, "%i\n", enabled);
210 }
211
212 static ssize_t show_lcd_level(struct device *dev,
213         struct device_attribute *attr, char *buf)
214 {
215         int ret;
216
217         ret = get_lcd_level();
218         if (ret < 0)
219                 return ret;
220
221         return sprintf(buf, "%i\n", ret);
222 }
223
224 static ssize_t store_lcd_level(struct device *dev,
225         struct device_attribute *attr, const char *buf, size_t count)
226 {
227         int level, ret;
228
229         if (sscanf(buf, "%i", &level) != 1 ||
230                 (level < 0 || level >= COMPAL_LCD_LEVEL_MAX))
231                 return -EINVAL;
232
233         ret = set_lcd_level(level);
234         if (ret < 0)
235                 return ret;
236
237         return count;
238 }
239
240 static ssize_t store_wlan_state(struct device *dev,
241         struct device_attribute *attr, const char *buf, size_t count)
242 {
243         int state, ret;
244
245         if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1))
246                 return -EINVAL;
247
248         ret = set_wlan_state(state);
249         if (ret < 0)
250                 return ret;
251
252         return count;
253 }
254
255 static ssize_t store_bluetooth_state(struct device *dev,
256         struct device_attribute *attr, const char *buf, size_t count)
257 {
258         int state, ret;
259
260         if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1))
261                 return -EINVAL;
262
263         ret = set_bluetooth_state(state);
264         if (ret < 0)
265                 return ret;
266
267         return count;
268 }
269
270 static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
271 static DEVICE_ATTR(bluetooth, 0644, show_bluetooth, store_bluetooth_state);
272 static DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan_state);
273 static DEVICE_ATTR(raw, 0444, show_raw, NULL);
274
275 static struct attribute *compal_attributes[] = {
276         &dev_attr_lcd_level.attr,
277         &dev_attr_bluetooth.attr,
278         &dev_attr_wlan.attr,
279         &dev_attr_raw.attr,
280         NULL
281 };
282
283 static struct attribute_group compal_attribute_group = {
284         .attrs = compal_attributes
285 };
286
287 static struct platform_driver compal_driver = {
288         .driver = {
289                 .name = "compal-laptop",
290                 .owner = THIS_MODULE,
291         }
292 };
293
294 static struct platform_device *compal_device;
295
296 /* Initialization */
297
298 static int dmi_check_cb(const struct dmi_system_id *id)
299 {
300         printk(KERN_INFO "compal-laptop: Identified laptop model '%s'.\n",
301                 id->ident);
302
303         return 0;
304 }
305
306 static struct dmi_system_id __initdata compal_dmi_table[] = {
307         {
308                 .ident = "FL90/IFL90",
309                 .matches = {
310                         DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
311                         DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
312                 },
313                 .callback = dmi_check_cb
314         },
315         {
316                 .ident = "FL90/IFL90",
317                 .matches = {
318                         DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
319                         DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"),
320                 },
321                 .callback = dmi_check_cb
322         },
323         {
324                 .ident = "FL91/IFL91",
325                 .matches = {
326                         DMI_MATCH(DMI_BOARD_NAME, "IFL91"),
327                         DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
328                 },
329                 .callback = dmi_check_cb
330         },
331         {
332                 .ident = "FL92/JFL92",
333                 .matches = {
334                         DMI_MATCH(DMI_BOARD_NAME, "JFL92"),
335                         DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
336                 },
337                 .callback = dmi_check_cb
338         },
339         {
340                 .ident = "FT00/IFT00",
341                 .matches = {
342                         DMI_MATCH(DMI_BOARD_NAME, "IFT00"),
343                         DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
344                 },
345                 .callback = dmi_check_cb
346         },
347         { }
348 };
349
350 static int __init compal_init(void)
351 {
352         int ret;
353
354         if (acpi_disabled)
355                 return -ENODEV;
356
357         if (!force && !dmi_check_system(compal_dmi_table))
358                 return -ENODEV;
359
360         /* Register backlight stuff */
361
362         compalbl_device = backlight_device_register("compal-laptop", NULL, NULL,
363                                                 &compalbl_ops);
364         if (IS_ERR(compalbl_device))
365                 return PTR_ERR(compalbl_device);
366
367         compalbl_device->props.max_brightness = COMPAL_LCD_LEVEL_MAX-1;
368
369         ret = platform_driver_register(&compal_driver);
370         if (ret)
371                 goto fail_backlight;
372
373         /* Register platform stuff */
374
375         compal_device = platform_device_alloc("compal-laptop", -1);
376         if (!compal_device) {
377                 ret = -ENOMEM;
378                 goto fail_platform_driver;
379         }
380
381         ret = platform_device_add(compal_device);
382         if (ret)
383                 goto fail_platform_device1;
384
385         ret = sysfs_create_group(&compal_device->dev.kobj,
386                 &compal_attribute_group);
387         if (ret)
388                 goto fail_platform_device2;
389
390         printk(KERN_INFO "compal-laptop: driver "COMPAL_DRIVER_VERSION
391                 " successfully loaded.\n");
392
393         return 0;
394
395 fail_platform_device2:
396
397         platform_device_del(compal_device);
398
399 fail_platform_device1:
400
401         platform_device_put(compal_device);
402
403 fail_platform_driver:
404
405         platform_driver_unregister(&compal_driver);
406
407 fail_backlight:
408
409         backlight_device_unregister(compalbl_device);
410
411         return ret;
412 }
413
414 static void __exit compal_cleanup(void)
415 {
416
417         sysfs_remove_group(&compal_device->dev.kobj, &compal_attribute_group);
418         platform_device_unregister(compal_device);
419         platform_driver_unregister(&compal_driver);
420         backlight_device_unregister(compalbl_device);
421
422         printk(KERN_INFO "compal-laptop: driver unloaded.\n");
423 }
424
425 module_init(compal_init);
426 module_exit(compal_cleanup);
427
428 MODULE_AUTHOR("Cezary Jackiewicz");
429 MODULE_DESCRIPTION("Compal Laptop Support");
430 MODULE_VERSION(COMPAL_DRIVER_VERSION);
431 MODULE_LICENSE("GPL");
432
433 MODULE_ALIAS("dmi:*:rnIFL90:rvrIFT00:*");
434 MODULE_ALIAS("dmi:*:rnIFL90:rvrREFERENCE:*");
435 MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*");
436 MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*");
437 MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*");