aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/compal-laptop.c
diff options
context:
space:
mode:
authorCezary Jackiewicz <cezary.jackiewicz@gmail.com>2008-06-09 19:22:22 -0400
committerAndi Kleen <andi@basil.nowhere.org>2008-07-16 17:27:01 -0400
commit5411552c707f4b7387ad63141ef3a559e7488091 (patch)
tree78b6fafe441413397ff33becd4bc144d461bc0a2 /drivers/misc/compal-laptop.c
parent9f1eb99c757939b0b1783f926130993e9c298bae (diff)
misc,acpi,backlight: compal Laptop Extras
This is driver for Compal Laptop: FL90/IFL90, based on MSI driver. This driver exports a few files in /sys/devices/platform/compal-laptop/: lcd_level - screen brightness: contains a single integer in the range 0..7 (rw) wlan - wlan subsystem state: contains 0 or 1 (rw) bluetooth - bluetooth subsystem state: contains 0 or 1 (rw) raw - raw value taken from embedded controller register (ro) In addition to these platform device attributes the driver registers itself in the Linux backlight control subsystem and is available to userspace under /sys/class/backlight/compal-laptop/. Signed-off-by: Cezary Jackiewicz <cezary.jackiewicz@gmail.com> Signed-off-by: Andi Kleen <ak@linux.intel.com> Cc: Richard Purdie <rpurdie@rpsys.net> Cc: Henrique de Moraes Holschuh <hmh@hmh.eng.br> Cc: Len Brown <lenb@kernel.org> Cc: Alexey Starikovskiy <aystarik@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/misc/compal-laptop.c')
-rw-r--r--drivers/misc/compal-laptop.c437
1 files changed, 437 insertions, 0 deletions
diff --git a/drivers/misc/compal-laptop.c b/drivers/misc/compal-laptop.c
new file mode 100644
index 000000000000..0c1f5875fbb9
--- /dev/null
+++ b/drivers/misc/compal-laptop.c
@@ -0,0 +1,437 @@
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
70static int force;
71module_param(force, bool, 0);
72MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
73
74/* Hardware access */
75
76static 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
86static 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
95static 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
114static 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
133static 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
158static int bl_get_brightness(struct backlight_device *b)
159{
160 return get_lcd_level();
161}
162
163
164static int bl_update_status(struct backlight_device *b)
165{
166 return set_lcd_level(b->props.brightness);
167}
168
169static struct backlight_ops compalbl_ops = {
170 .get_brightness = bl_get_brightness,
171 .update_status = bl_update_status,
172};
173
174static struct backlight_device *compalbl_device;
175
176/* Platform device */
177
178static 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
190static 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
200static 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
212static 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
224static 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
240static 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
255static 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
270static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
271static DEVICE_ATTR(bluetooth, 0644, show_bluetooth, store_bluetooth_state);
272static DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan_state);
273static DEVICE_ATTR(raw, 0444, show_raw, NULL);
274
275static 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
283static struct attribute_group compal_attribute_group = {
284 .attrs = compal_attributes
285};
286
287static struct platform_driver compal_driver = {
288 .driver = {
289 .name = "compal-laptop",
290 .owner = THIS_MODULE,
291 }
292};
293
294static struct platform_device *compal_device;
295
296/* Initialization */
297
298static 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
306static 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
350static 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
395fail_platform_device2:
396
397 platform_device_del(compal_device);
398
399fail_platform_device1:
400
401 platform_device_put(compal_device);
402
403fail_platform_driver:
404
405 platform_driver_unregister(&compal_driver);
406
407fail_backlight:
408
409 backlight_device_unregister(compalbl_device);
410
411 return ret;
412}
413
414static 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
425module_init(compal_init);
426module_exit(compal_cleanup);
427
428MODULE_AUTHOR("Cezary Jackiewicz");
429MODULE_DESCRIPTION("Compal Laptop Support");
430MODULE_VERSION(COMPAL_DRIVER_VERSION);
431MODULE_LICENSE("GPL");
432
433MODULE_ALIAS("dmi:*:rnIFL90:rvrIFT00:*");
434MODULE_ALIAS("dmi:*:rnIFL90:rvrREFERENCE:*");
435MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*");
436MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*");
437MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*");