d7b5330a7bcd00587a6f34819123a1e666642136
[powerpc.git] / drivers / misc / sony-laptop.c
1 /*
2  * ACPI Sony Notebook Control Driver (SNC)
3  *
4  * Copyright (C) 2004-2005 Stelian Pop <stelian@popies.net>
5  * Copyright (C) 2007 Mattia Dongili <malattia@linux.it>
6  *
7  * Parts of this driver inspired from asus_acpi.c and ibm_acpi.c
8  * which are copyrighted by their respective authors.
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,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU 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., 675 Mass Ave, Cambridge, MA 02139, USA.
23  *
24  */
25
26 #include <linux/kernel.h>
27 #include <linux/module.h>
28 #include <linux/moduleparam.h>
29 #include <linux/init.h>
30 #include <linux/types.h>
31 #include <linux/backlight.h>
32 #include <linux/platform_device.h>
33 #include <linux/err.h>
34 #include <acpi/acpi_drivers.h>
35 #include <acpi/acpi_bus.h>
36 #include <asm/uaccess.h>
37
38 #define ACPI_SNC_CLASS          "sony"
39 #define ACPI_SNC_HID            "SNY5001"
40 #define ACPI_SNC_DRIVER_NAME    "ACPI Sony Notebook Control Driver v0.4"
41
42 /* the device uses 1-based values, while the backlight subsystem uses
43    0-based values */
44 #define SONY_MAX_BRIGHTNESS     8
45
46 #define LOG_PFX                 KERN_WARNING "sony-laptop: "
47
48 MODULE_AUTHOR("Stelian Pop, Mattia Dongili");
49 MODULE_DESCRIPTION(ACPI_SNC_DRIVER_NAME);
50 MODULE_LICENSE("GPL");
51
52 static int debug;
53 module_param(debug, int, 0);
54 MODULE_PARM_DESC(debug, "set this to 1 (and RTFM) if you want to help "
55                  "the development of this driver");
56
57 static ssize_t sony_acpi_show(struct device *, struct device_attribute *,
58                               char *);
59 static ssize_t sony_acpi_store(struct device *, struct device_attribute *,
60                                const char *, size_t);
61
62 struct sony_acpi_value {
63         char *name;             /* name of the entry */
64         char **acpiget;         /* names of the ACPI get function */
65         char **acpiset;         /* names of the ACPI set function */
66         int min;                /* minimum allowed value or -1 */
67         int max;                /* maximum allowed value or -1 */
68         int value;              /* current setting */
69         int valid;              /* Has ever been set */
70         int debug;              /* active only in debug mode ? */
71         struct device_attribute devattr;        /* sysfs atribute */
72 };
73
74 #define HANDLE_NAMES(_name, _values...) \
75         static char *snc_##_name[] = { _values, NULL }
76
77 #define SONY_ACPI_VALUE(_name, _getters, _setters, _min, _max, _debug) \
78         { \
79                 .name           = __stringify(_name), \
80                 .acpiget        = _getters, \
81                 .acpiset        = _setters, \
82                 .min            = _min, \
83                 .max            = _max, \
84                 .debug          = _debug, \
85                 .devattr        = __ATTR(_name, 0, sony_acpi_show, sony_acpi_store), \
86         }
87
88 #define SONY_ACPI_VALUE_NULL    { .name = NULL }
89
90 HANDLE_NAMES(fnkey_get, "GHKE");
91
92 HANDLE_NAMES(brightness_def_get, "GPBR");
93 HANDLE_NAMES(brightness_def_set, "SPBR");
94
95 HANDLE_NAMES(cdpower_get, "GCDP");
96 HANDLE_NAMES(cdpower_set, "SCDP", "CDPW");
97
98 HANDLE_NAMES(audiopower_get, "GAZP");
99 HANDLE_NAMES(audiopower_set, "AZPW");
100
101 HANDLE_NAMES(lanpower_get, "GLNP");
102 HANDLE_NAMES(lanpower_set, "LNPW");
103
104 HANDLE_NAMES(PID_get, "GPID");
105
106 HANDLE_NAMES(CTR_get, "GCTR");
107 HANDLE_NAMES(CTR_set, "SCTR");
108
109 HANDLE_NAMES(PCR_get, "GPCR");
110 HANDLE_NAMES(PCR_set, "SPCR");
111
112 HANDLE_NAMES(CMI_get, "GCMI");
113 HANDLE_NAMES(CMI_set, "SCMI");
114
115 static struct sony_acpi_value sony_acpi_values[] = {
116         SONY_ACPI_VALUE(brightness_default, snc_brightness_def_get,
117                         snc_brightness_def_set, 1, SONY_MAX_BRIGHTNESS, 0),
118         SONY_ACPI_VALUE(fnkey, snc_fnkey_get, NULL, -1, -1, 0),
119         SONY_ACPI_VALUE(cdpower, snc_cdpower_get, snc_cdpower_set, 0, 1, 0),
120         SONY_ACPI_VALUE(audiopower, snc_audiopower_get, snc_audiopower_set, 0,
121                         1, 0),
122         SONY_ACPI_VALUE(lanpower, snc_lanpower_get, snc_lanpower_set, 0, 1, 1),
123         /* unknown methods */
124         SONY_ACPI_VALUE(PID, snc_PID_get, NULL, -1, -1, 1),
125         SONY_ACPI_VALUE(CTR, snc_CTR_get, snc_CTR_set, -1, -1, 1),
126         SONY_ACPI_VALUE(PCR, snc_PCR_get, snc_PCR_set, -1, -1, 1),
127         SONY_ACPI_VALUE(CMI, snc_CMI_get, snc_CMI_set, -1, -1, 1),
128         SONY_ACPI_VALUE_NULL
129 };
130
131 static acpi_handle sony_acpi_handle;
132 static struct acpi_device *sony_acpi_acpi_device = NULL;
133
134 /*
135  * acpi_evaluate_object wrappers
136  */
137 static int acpi_callgetfunc(acpi_handle handle, char *name, int *result)
138 {
139         struct acpi_buffer output;
140         union acpi_object out_obj;
141         acpi_status status;
142
143         output.length = sizeof(out_obj);
144         output.pointer = &out_obj;
145
146         status = acpi_evaluate_object(handle, name, NULL, &output);
147         if ((status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER)) {
148                 *result = out_obj.integer.value;
149                 return 0;
150         }
151
152         printk(LOG_PFX "acpi_callreadfunc failed\n");
153
154         return -1;
155 }
156
157 static int acpi_callsetfunc(acpi_handle handle, char *name, int value,
158                             int *result)
159 {
160         struct acpi_object_list params;
161         union acpi_object in_obj;
162         struct acpi_buffer output;
163         union acpi_object out_obj;
164         acpi_status status;
165
166         params.count = 1;
167         params.pointer = &in_obj;
168         in_obj.type = ACPI_TYPE_INTEGER;
169         in_obj.integer.value = value;
170
171         output.length = sizeof(out_obj);
172         output.pointer = &out_obj;
173
174         status = acpi_evaluate_object(handle, name, &params, &output);
175         if (status == AE_OK) {
176                 if (result != NULL) {
177                         if (out_obj.type != ACPI_TYPE_INTEGER) {
178                                 printk(LOG_PFX "acpi_evaluate_object bad "
179                                        "return type\n");
180                                 return -1;
181                         }
182                         *result = out_obj.integer.value;
183                 }
184                 return 0;
185         }
186
187         printk(LOG_PFX "acpi_evaluate_object failed\n");
188
189         return -1;
190 }
191
192 /*
193  * Sysfs show/store common to all sony_acpi_values
194  */
195 static ssize_t sony_acpi_show(struct device *dev, struct device_attribute *attr,
196                               char *buffer)
197 {
198         int value;
199         struct sony_acpi_value *item =
200             container_of(attr, struct sony_acpi_value, devattr);
201
202         if (!*item->acpiget)
203                 return -EIO;
204
205         if (acpi_callgetfunc(sony_acpi_handle, *item->acpiget, &value) < 0)
206                 return -EIO;
207
208         return snprintf(buffer, PAGE_SIZE, "%d\n", value);
209 }
210
211 static ssize_t sony_acpi_store(struct device *dev,
212                                struct device_attribute *attr,
213                                const char *buffer, size_t count)
214 {
215         int value;
216         struct sony_acpi_value *item =
217             container_of(attr, struct sony_acpi_value, devattr);
218
219         if (!item->acpiset)
220                 return -EIO;
221
222         if (count > 31)
223                 return -EINVAL;
224
225         value = simple_strtoul(buffer, NULL, 10);
226
227         if (item->min != -1 && value < item->min)
228                 return -EINVAL;
229         if (item->max != -1 && value > item->max)
230                 return -EINVAL;
231
232         if (acpi_callsetfunc(sony_acpi_handle, *item->acpiset, value, NULL) < 0)
233                 return -EIO;
234         item->value = value;
235         item->valid = 1;
236         return count;
237 }
238
239 /*
240  * Platform device
241  */
242 static struct platform_driver sncpf_driver = {
243         .driver = {
244                    .name = "sony-laptop",
245                    .owner = THIS_MODULE,
246                    }
247 };
248 static struct platform_device *sncpf_device;
249
250 static int sony_snc_pf_add(void)
251 {
252         acpi_handle handle;
253         struct sony_acpi_value *item;
254         int ret = 0;
255
256         ret = platform_driver_register(&sncpf_driver);
257         if (ret)
258                 goto out;
259
260         sncpf_device = platform_device_alloc("sony-laptop", -1);
261         if (!sncpf_device) {
262                 ret = -ENOMEM;
263                 goto out_platform_registered;
264         }
265
266         ret = platform_device_add(sncpf_device);
267         if (ret)
268                 goto out_platform_alloced;
269
270         for (item = sony_acpi_values; item->name; ++item) {
271
272                 if (!debug && item->debug)
273                         continue;
274
275                 /* find the available acpiget as described in the DSDT */
276                 for (; item->acpiget && *item->acpiget; ++item->acpiget) {
277                         if (ACPI_SUCCESS(acpi_get_handle(sony_acpi_handle,
278                                                          *item->acpiget,
279                                                          &handle))) {
280                                 if (debug)
281                                         printk(LOG_PFX "Found %s getter: %s\n",
282                                                item->name, *item->acpiget);
283                                 item->devattr.attr.mode |= S_IRUGO;
284                                 break;
285                         }
286                 }
287
288                 /* find the available acpiset as described in the DSDT */
289                 for (; item->acpiset && *item->acpiset; ++item->acpiset) {
290                         if (ACPI_SUCCESS(acpi_get_handle(sony_acpi_handle,
291                                                          *item->acpiset,
292                                                          &handle))) {
293                                 if (debug)
294                                         printk(LOG_PFX "Found %s setter: %s\n",
295                                                item->name, *item->acpiset);
296                                 item->devattr.attr.mode |= S_IWUSR;
297                                 break;
298                         }
299                 }
300
301                 if (item->devattr.attr.mode != 0) {
302                         ret =
303                             device_create_file(&sncpf_device->dev,
304                                                &item->devattr);
305                         if (ret)
306                                 goto out_sysfs;
307                 }
308         }
309
310         return 0;
311
312       out_sysfs:
313         for (item = sony_acpi_values; item->name; ++item) {
314                 device_remove_file(&sncpf_device->dev, &item->devattr);
315         }
316         platform_device_del(sncpf_device);
317       out_platform_alloced:
318         platform_device_put(sncpf_device);
319       out_platform_registered:
320         platform_driver_unregister(&sncpf_driver);
321       out:
322         return ret;
323 }
324
325 static void sony_snc_pf_remove(void)
326 {
327         struct sony_acpi_value *item;
328
329         for (item = sony_acpi_values; item->name; ++item) {
330                 device_remove_file(&sncpf_device->dev, &item->devattr);
331         }
332
333         platform_device_del(sncpf_device);
334         platform_device_put(sncpf_device);
335         platform_driver_unregister(&sncpf_driver);
336 }
337
338 /*
339  * Backlight device
340  */
341 static int sony_backlight_update_status(struct backlight_device *bd)
342 {
343         return acpi_callsetfunc(sony_acpi_handle, "SBRT",
344                                 bd->props->brightness + 1, NULL);
345 }
346
347 static int sony_backlight_get_brightness(struct backlight_device *bd)
348 {
349         int value;
350
351         if (acpi_callgetfunc(sony_acpi_handle, "GBRT", &value))
352                 return 0;
353         /* brightness levels are 1-based, while backlight ones are 0-based */
354         return value - 1;
355 }
356
357 static struct backlight_device *sony_backlight_device;
358 static struct backlight_properties sony_backlight_properties = {
359         .owner = THIS_MODULE,
360         .update_status = sony_backlight_update_status,
361         .get_brightness = sony_backlight_get_brightness,
362         .max_brightness = SONY_MAX_BRIGHTNESS - 1,
363 };
364
365 /*
366  * ACPI callbacks
367  */
368 static void sony_acpi_notify(acpi_handle handle, u32 event, void *data)
369 {
370         if (debug)
371                 printk(LOG_PFX "sony_acpi_notify, event: %d\n", event);
372         acpi_bus_generate_event(sony_acpi_acpi_device, 1, event);
373 }
374
375 static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
376                                       void *context, void **return_value)
377 {
378         struct acpi_namespace_node *node;
379         union acpi_operand_object *operand;
380
381         node = (struct acpi_namespace_node *)handle;
382         operand = (union acpi_operand_object *)node->object;
383
384         printk(LOG_PFX "method: name: %4.4s, args %X\n", node->name.ascii,
385                (u32) operand->method.param_count);
386
387         return AE_OK;
388 }
389
390 /*
391  * ACPI device
392  */
393 static int sony_acpi_resume(struct acpi_device *device)
394 {
395         struct sony_acpi_value *item;
396
397         for (item = sony_acpi_values; item->name; item++) {
398                 int ret;
399
400                 if (!item->valid)
401                         continue;
402                 ret = acpi_callsetfunc(sony_acpi_handle, *item->acpiset,
403                                        item->value, NULL);
404                 if (ret < 0) {
405                         printk("%s: %d\n", __FUNCTION__, ret);
406                         break;
407                 }
408         }
409         return 0;
410 }
411
412 static int sony_acpi_add(struct acpi_device *device)
413 {
414         acpi_status status;
415         int result;
416         acpi_handle handle;
417
418         sony_acpi_acpi_device = device;
419
420         sony_acpi_handle = device->handle;
421
422         if (debug) {
423                 status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_acpi_handle,
424                                              1, sony_walk_callback, NULL, NULL);
425                 if (ACPI_FAILURE(status)) {
426                         printk(LOG_PFX "unable to walk acpi resources\n");
427                         result = -ENODEV;
428                         goto outwalk;
429                 }
430         }
431
432         status = acpi_install_notify_handler(sony_acpi_handle,
433                                              ACPI_DEVICE_NOTIFY,
434                                              sony_acpi_notify, NULL);
435         if (ACPI_FAILURE(status)) {
436                 printk(LOG_PFX "unable to install notify handler\n");
437                 result = -ENODEV;
438                 goto outwalk;
439         }
440
441         if (ACPI_SUCCESS(acpi_get_handle(sony_acpi_handle, "GBRT", &handle))) {
442                 sony_backlight_device = backlight_device_register("sony", NULL,
443                                                                   NULL,
444                                                                   &sony_backlight_properties);
445
446                 if (IS_ERR(sony_backlight_device)) {
447                         printk(LOG_PFX "unable to register backlight device\n");
448                         sony_backlight_device = NULL;
449                 } else
450                         sony_backlight_properties.brightness =
451                             sony_backlight_get_brightness
452                             (sony_backlight_device);
453         }
454
455         if (sony_snc_pf_add())
456                 goto outbacklight;
457
458         printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully installed\n");
459
460         return 0;
461
462       outbacklight:
463         if (sony_backlight_device)
464                 backlight_device_unregister(sony_backlight_device);
465
466         status = acpi_remove_notify_handler(sony_acpi_handle,
467                                             ACPI_DEVICE_NOTIFY,
468                                             sony_acpi_notify);
469         if (ACPI_FAILURE(status))
470                 printk(LOG_PFX "unable to remove notify handler\n");
471       outwalk:
472         return result;
473 }
474
475 static int sony_acpi_remove(struct acpi_device *device, int type)
476 {
477         acpi_status status;
478
479         if (sony_backlight_device)
480                 backlight_device_unregister(sony_backlight_device);
481
482         sony_acpi_acpi_device = NULL;
483
484         status = acpi_remove_notify_handler(sony_acpi_handle,
485                                             ACPI_DEVICE_NOTIFY,
486                                             sony_acpi_notify);
487         if (ACPI_FAILURE(status))
488                 printk(LOG_PFX "unable to remove notify handler\n");
489
490         sony_snc_pf_remove();
491
492         printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully removed\n");
493
494         return 0;
495 }
496
497 static struct acpi_driver sony_acpi_driver = {
498         .name = ACPI_SNC_DRIVER_NAME,
499         .class = ACPI_SNC_CLASS,
500         .ids = ACPI_SNC_HID,
501         .ops = {
502                 .add = sony_acpi_add,
503                 .remove = sony_acpi_remove,
504                 .resume = sony_acpi_resume,
505                 },
506 };
507
508 static int __init sony_acpi_init(void)
509 {
510         return acpi_bus_register_driver(&sony_acpi_driver);
511 }
512
513 static void __exit sony_acpi_exit(void)
514 {
515         acpi_bus_unregister_driver(&sony_acpi_driver);
516 }
517
518 module_init(sony_acpi_init);
519 module_exit(sony_acpi_exit);