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