fujitsu-laptop.c 31.2 KB
Newer Older
1
2
3
/*-*-linux-c-*-*/

/*
Jonathan Woithe's avatar
Jonathan Woithe committed
4
  Copyright (C) 2007,2008 Jonathan Woithe <jwoithe@just42.net>
Jonathan Woithe's avatar
Jonathan Woithe committed
5
  Copyright (C) 2008 Peter Gruber <nokos@gmx.net>
6
  Copyright (C) 2008 Tony Vroon <tony@linx.net>
7
8
9
10
  Based on earlier work:
    Copyright (C) 2003 Shane Spencer <shane@bogomip.com>
    Adrian Yee <brewt-fujitsu@brewt.org>

Jonathan Woithe's avatar
Jonathan Woithe committed
11
12
  Templated from msi-laptop.c and thinkpad_acpi.c which is copyright
  by its respective authors.
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  02110-1301, USA.
 */

/*
 * fujitsu-laptop.c - Fujitsu laptop support, providing access to additional
 * features made available on a range of Fujitsu laptops including the
 * P2xxx/P5xxx/S6xxx/S7xxx series.
 *
 * This driver exports a few files in /sys/devices/platform/fujitsu-laptop/;
 * others may be added at a later date.
 *
 *   lcd_level - Screen brightness: contains a single integer in the
 *   range 0..7. (rw)
 *
 * 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/fujitsu-laptop/.
 *
Jonathan Woithe's avatar
Jonathan Woithe committed
45
46
47
 * Hotkeys present on certain Fujitsu laptops (eg: the S6xxx series) are
 * also supported by this driver.
 *
48
49
50
 * This driver has been tested on a Fujitsu Lifebook S6410, S7020 and
 * P8010.  It should work on most P-series and S-series Lifebooks, but
 * YMMV.
Jonathan Woithe's avatar
Jonathan Woithe committed
51
52
53
54
55
56
 *
 * The module parameter use_alt_lcd_levels switches between different ACPI
 * brightness controls which are used by different Fujitsu laptops.  In most
 * cases the correct method is automatically detected. "use_alt_lcd_levels=1"
 * is applicable for a Fujitsu Lifebook S6410 if autodetection fails.
 *
57
58
 */

59
60
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

61
62
63
64
65
66
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/acpi.h>
#include <linux/dmi.h>
#include <linux/backlight.h>
Jonathan Woithe's avatar
Jonathan Woithe committed
67
68
#include <linux/input.h>
#include <linux/kfifo.h>
69
#include <linux/platform_device.h>
70
#include <linux/slab.h>
71
#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
72
73
#include <linux/leds.h>
#endif
74

75
#define FUJITSU_DRIVER_VERSION "0.6.0"
76
77
78
79
80

#define FUJITSU_LCD_N_LEVELS 8

#define ACPI_FUJITSU_CLASS              "fujitsu"
#define ACPI_FUJITSU_HID                "FUJ02B1"
Jonathan Woithe's avatar
Jonathan Woithe committed
81
#define ACPI_FUJITSU_DRIVER_NAME	"Fujitsu laptop FUJ02B1 ACPI brightness driver"
82
#define ACPI_FUJITSU_DEVICE_NAME        "Fujitsu FUJ02B1"
Jonathan Woithe's avatar
Jonathan Woithe committed
83
84
85
86
87
88
89
90
91
#define ACPI_FUJITSU_HOTKEY_HID 	"FUJ02E3"
#define ACPI_FUJITSU_HOTKEY_DRIVER_NAME "Fujitsu laptop FUJ02E3 ACPI hotkeys driver"
#define ACPI_FUJITSU_HOTKEY_DEVICE_NAME "Fujitsu FUJ02E3"

#define ACPI_FUJITSU_NOTIFY_CODE1     0x80

#define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS     0x86
#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS     0x87

92
93
94
95
96
97
98
99
100
/* FUNC interface - command values */
#define FUNC_RFKILL	0x1000
#define FUNC_LEDS	0x1001
#define FUNC_BUTTONS	0x1002
#define FUNC_BACKLIGHT  0x1004

/* FUNC interface - responses */
#define UNSUPPORTED_CMD 0x80000000

101
#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
102
103
104
105
106
107
108
109
/* FUNC interface - LED control */
#define FUNC_LED_OFF	0x1
#define FUNC_LED_ON	0x30001
#define KEYBOARD_LAMPS	0x100
#define LOGOLAMP_POWERON 0x2000
#define LOGOLAMP_ALWAYS  0x4000
#endif

Jonathan Woithe's avatar
Jonathan Woithe committed
110
/* Hotkey details */
111
112
113
114
#define KEY1_CODE	0x410	/* codes for the keys in the GIRB register */
#define KEY2_CODE	0x411
#define KEY3_CODE	0x412
#define KEY4_CODE	0x413
Jonathan Woithe's avatar
Jonathan Woithe committed
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131

#define MAX_HOTKEY_RINGBUFFER_SIZE 100
#define RINGBUFFERSIZE 40

/* Debugging */
#define FUJLAPTOP_LOG	   ACPI_FUJITSU_HID ": "
#define FUJLAPTOP_ERR	   KERN_ERR FUJLAPTOP_LOG
#define FUJLAPTOP_NOTICE   KERN_NOTICE FUJLAPTOP_LOG
#define FUJLAPTOP_INFO	   KERN_INFO FUJLAPTOP_LOG
#define FUJLAPTOP_DEBUG    KERN_DEBUG FUJLAPTOP_LOG

#define FUJLAPTOP_DBG_ALL	  0xffff
#define FUJLAPTOP_DBG_ERROR	  0x0001
#define FUJLAPTOP_DBG_WARN	  0x0002
#define FUJLAPTOP_DBG_INFO	  0x0004
#define FUJLAPTOP_DBG_TRACE	  0x0008

132
133
#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
#define vdbg_printk(a_dbg_level, format, arg...) \
Jonathan Woithe's avatar
Jonathan Woithe committed
134
135
136
137
	do { if (dbg_level & a_dbg_level) \
		printk(FUJLAPTOP_DEBUG "%s: " format, __func__ , ## arg); \
	} while (0)
#else
138
139
#define vdbg_printk(a_dbg_level, format, arg...) \
	do { } while (0)
Jonathan Woithe's avatar
Jonathan Woithe committed
140
141
142
#endif

/* Device controlling the backlight and associated keys */
143
144
struct fujitsu_t {
	acpi_handle acpi_handle;
Jonathan Woithe's avatar
Jonathan Woithe committed
145
146
147
	struct acpi_device *dev;
	struct input_dev *input;
	char phys[32];
148
149
	struct backlight_device *bl_device;
	struct platform_device *pf_device;
150
	int keycode1, keycode2, keycode3, keycode4;
151

Jonathan Woithe's avatar
Jonathan Woithe committed
152
	unsigned int max_brightness;
153
154
155
156
157
	unsigned int brightness_changed;
	unsigned int brightness_level;
};

static struct fujitsu_t *fujitsu;
Jonathan Woithe's avatar
Jonathan Woithe committed
158
159
160
161
162
163
164
165
166
167
static int use_alt_lcd_levels = -1;
static int disable_brightness_adjust = -1;

/* Device used to access other hotkeys on the laptop */
struct fujitsu_hotkey_t {
	acpi_handle acpi_handle;
	struct acpi_device *dev;
	struct input_dev *input;
	char phys[32];
	struct platform_device *pf_device;
168
	struct kfifo fifo;
Jonathan Woithe's avatar
Jonathan Woithe committed
169
	spinlock_t fifo_lock;
170
	int rfkill_supported;
171
172
173
	int rfkill_state;
	int logolamp_registered;
	int kblamps_registered;
Jonathan Woithe's avatar
Jonathan Woithe committed
174
};
175

Jonathan Woithe's avatar
Jonathan Woithe committed
176
177
static struct fujitsu_hotkey_t *fujitsu_hotkey;

178
static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event);
Jonathan Woithe's avatar
Jonathan Woithe committed
179

180
#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
181
182
183
184
static enum led_brightness logolamp_get(struct led_classdev *cdev);
static void logolamp_set(struct led_classdev *cdev,
			       enum led_brightness brightness);

185
static struct led_classdev logolamp_led = {
186
187
188
189
190
191
192
193
194
 .name = "fujitsu::logolamp",
 .brightness_get = logolamp_get,
 .brightness_set = logolamp_set
};

static enum led_brightness kblamps_get(struct led_classdev *cdev);
static void kblamps_set(struct led_classdev *cdev,
			       enum led_brightness brightness);

195
static struct led_classdev kblamps_led = {
196
197
198
199
200
201
 .name = "fujitsu::kblamps",
 .brightness_get = kblamps_get,
 .brightness_set = kblamps_set
};
#endif

Jonathan Woithe's avatar
Jonathan Woithe committed
202
203
204
205
#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
static u32 dbg_level = 0x03;
#endif

206
static void acpi_fujitsu_notify(struct acpi_device *device, u32 event);
Jonathan Woithe's avatar
Jonathan Woithe committed
207

208
209
210
211
212
213
214
215
216
217
218
219
/* Fujitsu ACPI interface function */

static int call_fext_func(int cmd, int arg0, int arg1, int arg2)
{
	acpi_status status = AE_OK;
	union acpi_object params[4] = {
	{ .type = ACPI_TYPE_INTEGER },
	{ .type = ACPI_TYPE_INTEGER },
	{ .type = ACPI_TYPE_INTEGER },
	{ .type = ACPI_TYPE_INTEGER }
	};
	struct acpi_object_list arg_list = { 4, &params[0] };
220
	unsigned long long value;
221
222
223
224
225
226
227
228
229
230
231
232
233
234
	acpi_handle handle = NULL;

	status = acpi_get_handle(fujitsu_hotkey->acpi_handle, "FUNC", &handle);
	if (ACPI_FAILURE(status)) {
		vdbg_printk(FUJLAPTOP_DBG_ERROR,
				"FUNC interface is not present\n");
		return -ENODEV;
	}

	params[0].integer.value = cmd;
	params[1].integer.value = arg0;
	params[2].integer.value = arg1;
	params[3].integer.value = arg2;

235
	status = acpi_evaluate_integer(handle, NULL, &arg_list, &value);
236
237
238
239
240
241
242
243
244
	if (ACPI_FAILURE(status)) {
		vdbg_printk(FUJLAPTOP_DBG_WARN,
			"FUNC 0x%x (args 0x%x, 0x%x, 0x%x) call failed\n",
				cmd, arg0, arg1, arg2);
		return -ENODEV;
	}

	vdbg_printk(FUJLAPTOP_DBG_TRACE,
		"FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n",
245
246
			cmd, arg0, arg1, arg2, (int)value);
	return value;
247
248
}

249
#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
/* LED class callbacks */

static void logolamp_set(struct led_classdev *cdev,
			       enum led_brightness brightness)
{
	if (brightness >= LED_FULL) {
		call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON);
		call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_ON);
	} else if (brightness >= LED_HALF) {
		call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON);
		call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_OFF);
	} else {
		call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_OFF);
	}
}

static void kblamps_set(struct led_classdev *cdev,
			       enum led_brightness brightness)
{
	if (brightness >= LED_FULL)
		call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_ON);
	else
		call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_OFF);
}

static enum led_brightness logolamp_get(struct led_classdev *cdev)
{
	enum led_brightness brightness = LED_OFF;
	int poweron, always;

	poweron = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0);
	if (poweron == FUNC_LED_ON) {
		brightness = LED_HALF;
		always = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0);
		if (always == FUNC_LED_ON)
			brightness = LED_FULL;
	}
	return brightness;
}

static enum led_brightness kblamps_get(struct led_classdev *cdev)
{
	enum led_brightness brightness = LED_OFF;

	if (call_fext_func(FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON)
		brightness = LED_FULL;

	return brightness;
}
#endif

Jonathan Woithe's avatar
Jonathan Woithe committed
301
/* Hardware access for LCD brightness control */
302
303
304
305
306
307

static int set_lcd_level(int level)
{
	acpi_status status = AE_OK;
	acpi_handle handle = NULL;

Jonathan Woithe's avatar
Jonathan Woithe committed
308
309
310
311
	vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBLL [%d]\n",
		    level);

	if (level < 0 || level >= fujitsu->max_brightness)
312
313
314
315
		return -EINVAL;

	status = acpi_get_handle(fujitsu->acpi_handle, "SBLL", &handle);
	if (ACPI_FAILURE(status)) {
Jonathan Woithe's avatar
Jonathan Woithe committed
316
317
318
319
320
		vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBLL not present\n");
		return -ENODEV;
	}


321
	status = acpi_execute_simple_method(handle, NULL, level);
Jonathan Woithe's avatar
Jonathan Woithe committed
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
	if (ACPI_FAILURE(status))
		return -ENODEV;

	return 0;
}

static int set_lcd_level_alt(int level)
{
	acpi_status status = AE_OK;
	acpi_handle handle = NULL;

	vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBL2 [%d]\n",
		    level);

	if (level < 0 || level >= fujitsu->max_brightness)
		return -EINVAL;

	status = acpi_get_handle(fujitsu->acpi_handle, "SBL2", &handle);
	if (ACPI_FAILURE(status)) {
		vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBL2 not present\n");
342
343
344
		return -ENODEV;
	}

345
	status = acpi_execute_simple_method(handle, NULL, level);
346
347
348
349
350
351
352
353
	if (ACPI_FAILURE(status))
		return -ENODEV;

	return 0;
}

static int get_lcd_level(void)
{
354
	unsigned long long state = 0;
355
356
	acpi_status status = AE_OK;

Jonathan Woithe's avatar
Jonathan Woithe committed
357
358
	vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLL\n");

359
360
	status =
	    acpi_evaluate_integer(fujitsu->acpi_handle, "GBLL", NULL, &state);
361
362
	if (ACPI_FAILURE(status))
		return 0;
363

Jonathan Woithe's avatar
Jonathan Woithe committed
364
365
366
367
368
369
370
371
372
373
374
375
	fujitsu->brightness_level = state & 0x0fffffff;

	if (state & 0x80000000)
		fujitsu->brightness_changed = 1;
	else
		fujitsu->brightness_changed = 0;

	return fujitsu->brightness_level;
}

static int get_max_brightness(void)
{
376
	unsigned long long state = 0;
Jonathan Woithe's avatar
Jonathan Woithe committed
377
378
379
380
381
382
	acpi_status status = AE_OK;

	vdbg_printk(FUJLAPTOP_DBG_TRACE, "get max lcd level via RBLL\n");

	status =
	    acpi_evaluate_integer(fujitsu->acpi_handle, "RBLL", NULL, &state);
383
384
	if (ACPI_FAILURE(status))
		return -1;
Jonathan Woithe's avatar
Jonathan Woithe committed
385
386
387
388
389
390

	fujitsu->max_brightness = state;

	return fujitsu->max_brightness;
}

391
392
393
394
/* Backlight device stuff */

static int bl_get_brightness(struct backlight_device *b)
{
395
	return get_lcd_level();
396
397
398
399
}

static int bl_update_status(struct backlight_device *b)
{
400
401
402
403
404
405
406
407
408
409
	int ret;
	if (b->props.power == 4)
		ret = call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x3);
	else
		ret = call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x0);
	if (ret != 0)
		vdbg_printk(FUJLAPTOP_DBG_ERROR,
			"Unable to adjust backlight power, error code %i\n",
			ret);

Jonathan Woithe's avatar
Jonathan Woithe committed
410
	if (use_alt_lcd_levels)
411
		ret = set_lcd_level_alt(b->props.brightness);
Jonathan Woithe's avatar
Jonathan Woithe committed
412
	else
413
414
415
416
417
418
		ret = set_lcd_level(b->props.brightness);
	if (ret != 0)
		vdbg_printk(FUJLAPTOP_DBG_ERROR,
			"Unable to adjust LCD brightness, error code %i\n",
			ret);
	return ret;
419
420
}

421
static const struct backlight_ops fujitsubl_ops = {
422
423
424
425
	.get_brightness = bl_get_brightness,
	.update_status = bl_update_status,
};

Jonathan Woithe's avatar
Jonathan Woithe committed
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
/* Platform LCD brightness device */

static ssize_t
show_max_brightness(struct device *dev,
		    struct device_attribute *attr, char *buf)
{

	int ret;

	ret = get_max_brightness();
	if (ret < 0)
		return ret;

	return sprintf(buf, "%i\n", ret);
}

static ssize_t
show_brightness_changed(struct device *dev,
			struct device_attribute *attr, char *buf)
{

	int ret;

	ret = fujitsu->brightness_changed;
	if (ret < 0)
		return ret;

	return sprintf(buf, "%i\n", ret);
}
455
456
457
458
459
460
461

static ssize_t show_lcd_level(struct device *dev,
			      struct device_attribute *attr, char *buf)
{

	int ret;

462
	ret = get_lcd_level();
463
464
465
466
467
468
469
470
471
472
473
474
475
476
	if (ret < 0)
		return ret;

	return sprintf(buf, "%i\n", ret);
}

static ssize_t store_lcd_level(struct device *dev,
			       struct device_attribute *attr, const char *buf,
			       size_t count)
{

	int level, ret;

	if (sscanf(buf, "%i", &level) != 1
Jonathan Woithe's avatar
Jonathan Woithe committed
477
	    || (level < 0 || level >= fujitsu->max_brightness))
478
479
		return -EINVAL;

Jonathan Woithe's avatar
Jonathan Woithe committed
480
481
482
483
484
485
486
	if (use_alt_lcd_levels)
		ret = set_lcd_level_alt(level);
	else
		ret = set_lcd_level(level);
	if (ret < 0)
		return ret;

487
	ret = get_lcd_level();
488
489
490
491
492
493
	if (ret < 0)
		return ret;

	return count;
}

494
495
496
static ssize_t
ignore_store(struct device *dev,
	     struct device_attribute *attr, const char *buf, size_t count)
Jonathan Woithe's avatar
Jonathan Woithe committed
497
{
498
499
	return count;
}
Jonathan Woithe's avatar
Jonathan Woithe committed
500

501
502
503
504
static ssize_t
show_lid_state(struct device *dev,
			struct device_attribute *attr, char *buf)
{
505
	if (!(fujitsu_hotkey->rfkill_supported & 0x100))
506
507
508
509
510
511
		return sprintf(buf, "unknown\n");
	if (fujitsu_hotkey->rfkill_state & 0x100)
		return sprintf(buf, "open\n");
	else
		return sprintf(buf, "closed\n");
}
Jonathan Woithe's avatar
Jonathan Woithe committed
512

513
514
515
516
static ssize_t
show_dock_state(struct device *dev,
			struct device_attribute *attr, char *buf)
{
517
	if (!(fujitsu_hotkey->rfkill_supported & 0x200))
518
519
520
521
522
		return sprintf(buf, "unknown\n");
	if (fujitsu_hotkey->rfkill_state & 0x200)
		return sprintf(buf, "docked\n");
	else
		return sprintf(buf, "undocked\n");
Jonathan Woithe's avatar
Jonathan Woithe committed
523
524
525
}

static ssize_t
526
527
show_radios_state(struct device *dev,
			struct device_attribute *attr, char *buf)
Jonathan Woithe's avatar
Jonathan Woithe committed
528
{
529
	if (!(fujitsu_hotkey->rfkill_supported & 0x20))
530
531
532
533
534
		return sprintf(buf, "unknown\n");
	if (fujitsu_hotkey->rfkill_state & 0x20)
		return sprintf(buf, "on\n");
	else
		return sprintf(buf, "killed\n");
Jonathan Woithe's avatar
Jonathan Woithe committed
535
536
537
538
539
}

static DEVICE_ATTR(max_brightness, 0444, show_max_brightness, ignore_store);
static DEVICE_ATTR(brightness_changed, 0444, show_brightness_changed,
		   ignore_store);
540
static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
541
542
543
static DEVICE_ATTR(lid, 0444, show_lid_state, ignore_store);
static DEVICE_ATTR(dock, 0444, show_dock_state, ignore_store);
static DEVICE_ATTR(radios, 0444, show_radios_state, ignore_store);
544
545

static struct attribute *fujitsupf_attributes[] = {
Jonathan Woithe's avatar
Jonathan Woithe committed
546
547
	&dev_attr_brightness_changed.attr,
	&dev_attr_max_brightness.attr,
548
	&dev_attr_lcd_level.attr,
549
550
551
	&dev_attr_lid.attr,
	&dev_attr_dock.attr,
	&dev_attr_radios.attr,
552
553
554
555
556
557
558
559
560
561
562
563
564
565
	NULL
};

static struct attribute_group fujitsupf_attribute_group = {
	.attrs = fujitsupf_attributes
};

static struct platform_driver fujitsupf_driver = {
	.driver = {
		   .name = "fujitsu-laptop",
		   .owner = THIS_MODULE,
		   }
};

566
static void __init dmi_check_cb_common(const struct dmi_system_id *id)
Jonathan Woithe's avatar
Jonathan Woithe committed
567
{
568
	pr_info("Identified laptop model '%s'\n", id->ident);
Jonathan Woithe's avatar
Jonathan Woithe committed
569
	if (use_alt_lcd_levels == -1) {
570
571
		if (acpi_has_method(NULL,
				"\\_SB.PCI0.LPCB.FJEX.SBL2"))
572
573
574
575
576
			use_alt_lcd_levels = 1;
		else
			use_alt_lcd_levels = 0;
		vdbg_printk(FUJLAPTOP_DBG_TRACE, "auto-detected usealt as "
			"%i\n", use_alt_lcd_levels);
Jonathan Woithe's avatar
Jonathan Woithe committed
577
	}
578
579
}

580
static int __init dmi_check_cb_s6410(const struct dmi_system_id *id)
581
582
583
584
{
	dmi_check_cb_common(id);
	fujitsu->keycode1 = KEY_SCREENLOCK;	/* "Lock" */
	fujitsu->keycode2 = KEY_HELP;	/* "Mobility Center" */
585
	return 1;
586
587
}

588
static int __init dmi_check_cb_s6420(const struct dmi_system_id *id)
589
590
591
592
{
	dmi_check_cb_common(id);
	fujitsu->keycode1 = KEY_SCREENLOCK;	/* "Lock" */
	fujitsu->keycode2 = KEY_HELP;	/* "Mobility Center" */
593
	return 1;
594
595
}

596
static int __init dmi_check_cb_p8010(const struct dmi_system_id *id)
597
598
599
600
601
{
	dmi_check_cb_common(id);
	fujitsu->keycode1 = KEY_HELP;	/* "Support" */
	fujitsu->keycode3 = KEY_SWITCHVIDEOMODE;	/* "Presentation" */
	fujitsu->keycode4 = KEY_WWW;	/* "Internet" */
602
	return 1;
Jonathan Woithe's avatar
Jonathan Woithe committed
603
604
}

605
static const struct dmi_system_id fujitsu_dmi_table[] __initconst = {
Jonathan Woithe's avatar
Jonathan Woithe committed
606
	{
607
	 .ident = "Fujitsu Siemens S6410",
Jonathan Woithe's avatar
Jonathan Woithe committed
608
609
610
611
612
	 .matches = {
		     DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
		     DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6410"),
		     },
	 .callback = dmi_check_cb_s6410},
613
614
615
616
617
618
619
	{
	 .ident = "Fujitsu Siemens S6420",
	 .matches = {
		     DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
		     DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6420"),
		     },
	 .callback = dmi_check_cb_s6420},
620
	{
621
	 .ident = "Fujitsu LifeBook P8010",
622
623
624
	 .matches = {
		     DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
		     DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P8010"),
625
626
		     },
	 .callback = dmi_check_cb_p8010},
Jonathan Woithe's avatar
Jonathan Woithe committed
627
628
629
630
	{}
};

/* ACPI device for LCD brightness control */
631

632
static int acpi_fujitsu_add(struct acpi_device *device)
633
634
{
	int state = 0;
Jonathan Woithe's avatar
Jonathan Woithe committed
635
636
	struct input_dev *input;
	int error;
637
638
639
640
641
642
643

	if (!device)
		return -EINVAL;

	fujitsu->acpi_handle = device->handle;
	sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_DEVICE_NAME);
	sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
644
	device->driver_data = fujitsu;
645

Jonathan Woithe's avatar
Jonathan Woithe committed
646
647
648
	fujitsu->input = input = input_allocate_device();
	if (!input) {
		error = -ENOMEM;
649
		goto err_stop;
Jonathan Woithe's avatar
Jonathan Woithe committed
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
	}

	snprintf(fujitsu->phys, sizeof(fujitsu->phys),
		 "%s/video/input0", acpi_device_hid(device));

	input->name = acpi_device_name(device);
	input->phys = fujitsu->phys;
	input->id.bustype = BUS_HOST;
	input->id.product = 0x06;
	input->dev.parent = &device->dev;
	input->evbit[0] = BIT(EV_KEY);
	set_bit(KEY_BRIGHTNESSUP, input->keybit);
	set_bit(KEY_BRIGHTNESSDOWN, input->keybit);
	set_bit(KEY_UNKNOWN, input->keybit);

	error = input_register_device(input);
	if (error)
		goto err_free_input_dev;

669
670
	error = acpi_bus_update_power(fujitsu->acpi_handle, &state);
	if (error) {
671
		pr_err("Error reading power state\n");
672
		goto err_unregister_input_dev;
673
674
	}

675
	pr_info("ACPI: %s [%s] (%s)\n",
676
677
678
	       acpi_device_name(device), acpi_device_bid(device),
	       !device->power.state ? "on" : "off");

Jonathan Woithe's avatar
Jonathan Woithe committed
679
680
	fujitsu->dev = device;

681
	if (acpi_has_method(device->handle, METHOD_NAME__INI)) {
Jonathan Woithe's avatar
Jonathan Woithe committed
682
683
684
685
		vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
		if (ACPI_FAILURE
		    (acpi_evaluate_object
		     (device->handle, METHOD_NAME__INI, NULL, NULL)))
686
			pr_err("_INI Method failed\n");
Jonathan Woithe's avatar
Jonathan Woithe committed
687
688
689
690
691
692
	}

	/* do config (detect defaults) */
	use_alt_lcd_levels = use_alt_lcd_levels == 1 ? 1 : 0;
	disable_brightness_adjust = disable_brightness_adjust == 1 ? 1 : 0;
	vdbg_printk(FUJLAPTOP_DBG_INFO,
693
694
		    "config: [alt interface: %d], [adjust disable: %d]\n",
		    use_alt_lcd_levels, disable_brightness_adjust);
Jonathan Woithe's avatar
Jonathan Woithe committed
695
696
697

	if (get_max_brightness() <= 0)
		fujitsu->max_brightness = FUJITSU_LCD_N_LEVELS;
698
	get_lcd_level();
Jonathan Woithe's avatar
Jonathan Woithe committed
699

700
	return 0;
Jonathan Woithe's avatar
Jonathan Woithe committed
701

702
703
err_unregister_input_dev:
	input_unregister_device(input);
704
	input = NULL;
Jonathan Woithe's avatar
Jonathan Woithe committed
705
706
707
err_free_input_dev:
	input_free_device(input);
err_stop:
708
	return error;
709
710
}

711
static int acpi_fujitsu_remove(struct acpi_device *device)
712
{
713
714
	struct fujitsu_t *fujitsu = acpi_driver_data(device);
	struct input_dev *input = fujitsu->input;
715

716
	input_unregister_device(input);
Jonathan Woithe's avatar
Jonathan Woithe committed
717

Al Viro's avatar
Al Viro committed
718
	fujitsu->acpi_handle = NULL;
719
720
721
722

	return 0;
}

Jonathan Woithe's avatar
Jonathan Woithe committed
723
724
/* Brightness notify */

725
static void acpi_fujitsu_notify(struct acpi_device *device, u32 event)
Jonathan Woithe's avatar
Jonathan Woithe committed
726
727
728
729
730
731
732
733
734
735
736
{
	struct input_dev *input;
	int keycode;
	int oldb, newb;

	input = fujitsu->input;

	switch (event) {
	case ACPI_FUJITSU_NOTIFY_CODE1:
		keycode = 0;
		oldb = fujitsu->brightness_level;
737
		get_lcd_level();
Jonathan Woithe's avatar
Jonathan Woithe committed
738
739
740
741
742
743
		newb = fujitsu->brightness_level;

		vdbg_printk(FUJLAPTOP_DBG_TRACE,
			    "brightness button event [%i -> %i (%i)]\n",
			    oldb, newb, fujitsu->brightness_changed);

744
		if (oldb < newb) {
Jonathan Woithe's avatar
Jonathan Woithe committed
745
746
747
748
749
750
			if (disable_brightness_adjust != 1) {
				if (use_alt_lcd_levels)
					set_lcd_level_alt(newb);
				else
					set_lcd_level(newb);
			}
751
			keycode = KEY_BRIGHTNESSUP;
Jonathan Woithe's avatar
Jonathan Woithe committed
752
753
754
755
756
757
758
		} else if (oldb > newb) {
			if (disable_brightness_adjust != 1) {
				if (use_alt_lcd_levels)
					set_lcd_level_alt(newb);
				else
					set_lcd_level(newb);
			}
759
			keycode = KEY_BRIGHTNESSDOWN;
Jonathan Woithe's avatar
Jonathan Woithe committed
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
		}
		break;
	default:
		keycode = KEY_UNKNOWN;
		vdbg_printk(FUJLAPTOP_DBG_WARN,
			    "unsupported event [0x%x]\n", event);
		break;
	}

	if (keycode != 0) {
		input_report_key(input, keycode, 1);
		input_sync(input);
		input_report_key(input, keycode, 0);
		input_sync(input);
	}
}

/* ACPI device for hotkey handling */

static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
{
	int result = 0;
	int state = 0;
	struct input_dev *input;
	int error;
	int i;

	if (!device)
		return -EINVAL;

	fujitsu_hotkey->acpi_handle = device->handle;
	sprintf(acpi_device_name(device), "%s",
		ACPI_FUJITSU_HOTKEY_DEVICE_NAME);
	sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
794
	device->driver_data = fujitsu_hotkey;
Jonathan Woithe's avatar
Jonathan Woithe committed
795
796
797

	/* kfifo */
	spin_lock_init(&fujitsu_hotkey->fifo_lock);
798
	error = kfifo_alloc(&fujitsu_hotkey->fifo, RINGBUFFERSIZE * sizeof(int),
Stefani Seibold's avatar
Stefani Seibold committed
799
			GFP_KERNEL);
800
	if (error) {
801
		pr_err("kfifo_alloc failed\n");
Jonathan Woithe's avatar
Jonathan Woithe committed
802
803
804
805
806
807
		goto err_stop;
	}

	fujitsu_hotkey->input = input = input_allocate_device();
	if (!input) {
		error = -ENOMEM;
808
		goto err_free_fifo;
Jonathan Woithe's avatar
Jonathan Woithe committed
809
810
811
812
813
814
815
816
817
818
	}

	snprintf(fujitsu_hotkey->phys, sizeof(fujitsu_hotkey->phys),
		 "%s/video/input0", acpi_device_hid(device));

	input->name = acpi_device_name(device);
	input->phys = fujitsu_hotkey->phys;
	input->id.bustype = BUS_HOST;
	input->id.product = 0x06;
	input->dev.parent = &device->dev;
819
820

	set_bit(EV_KEY, input->evbit);
821
822
823
824
	set_bit(fujitsu->keycode1, input->keybit);
	set_bit(fujitsu->keycode2, input->keybit);
	set_bit(fujitsu->keycode3, input->keybit);
	set_bit(fujitsu->keycode4, input->keybit);
Jonathan Woithe's avatar
Jonathan Woithe committed
825
826
827
828
829
830
	set_bit(KEY_UNKNOWN, input->keybit);

	error = input_register_device(input);
	if (error)
		goto err_free_input_dev;

831
832
	error = acpi_bus_update_power(fujitsu_hotkey->acpi_handle, &state);
	if (error) {
833
		pr_err("Error reading power state\n");
834
		goto err_unregister_input_dev;
Jonathan Woithe's avatar
Jonathan Woithe committed
835
836
	}

837
838
839
	pr_info("ACPI: %s [%s] (%s)\n",
		acpi_device_name(device), acpi_device_bid(device),
		!device->power.state ? "on" : "off");
Jonathan Woithe's avatar
Jonathan Woithe committed
840
841
842

	fujitsu_hotkey->dev = device;

843
	if (acpi_has_method(device->handle, METHOD_NAME__INI)) {
Jonathan Woithe's avatar
Jonathan Woithe committed
844
845
846
847
		vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
		if (ACPI_FAILURE
		    (acpi_evaluate_object
		     (device->handle, METHOD_NAME__INI, NULL, NULL)))
848
			pr_err("_INI Method failed\n");
Jonathan Woithe's avatar
Jonathan Woithe committed
849
850
	}

851
852
853
854
	i = 0;
	while (call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0
		&& (i++) < MAX_HOTKEY_RINGBUFFER_SIZE)
		; /* No action, result is discarded */
Jonathan Woithe's avatar
Jonathan Woithe committed
855
856
	vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i);

857
858
859
860
861
862
863
864
865
866
867
	fujitsu_hotkey->rfkill_supported =
		call_fext_func(FUNC_RFKILL, 0x0, 0x0, 0x0);

	/* Make sure our bitmask of supported functions is cleared if the
	   RFKILL function block is not implemented, like on the S7020. */
	if (fujitsu_hotkey->rfkill_supported == UNSUPPORTED_CMD)
		fujitsu_hotkey->rfkill_supported = 0;

	if (fujitsu_hotkey->rfkill_supported)
		fujitsu_hotkey->rfkill_state =
			call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0);
868
869

	/* Suspect this is a keymap of the application panel, print it */
870
	pr_info("BTNI: [0x%x]\n", call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0));
871

872
#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
873
874
875
876
877
878
	if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) {
		result = led_classdev_register(&fujitsu->pf_device->dev,
						&logolamp_led);
		if (result == 0) {
			fujitsu_hotkey->logolamp_registered = 1;
		} else {
879
880
			pr_err("Could not register LED handler for logo lamp, error %i\n",
			       result);
881
882
883
884
885
886
887
888
889
890
		}
	}

	if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) &&
	   (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) {
		result = led_classdev_register(&fujitsu->pf_device->dev,
						&kblamps_led);
		if (result == 0) {
			fujitsu_hotkey->kblamps_registered = 1;
		} else {
891
892
			pr_err("Could not register LED handler for keyboard lamps, error %i\n",
			       result);
893
894
		}
	}
895
#endif
896

Jonathan Woithe's avatar
Jonathan Woithe committed
897
898
	return result;

899
900
err_unregister_input_dev:
	input_unregister_device(input);
901
	input = NULL;
Jonathan Woithe's avatar
Jonathan Woithe committed
902
903
err_free_input_dev:
	input_free_device(input);
904
err_free_fifo:
905
	kfifo_free(&fujitsu_hotkey->fifo);
Jonathan Woithe's avatar
Jonathan Woithe committed
906
err_stop:
907
	return error;
Jonathan Woithe's avatar
Jonathan Woithe committed
908
909
}

910
static int acpi_fujitsu_hotkey_remove(struct acpi_device *device)
Jonathan Woithe's avatar
Jonathan Woithe committed
911
{
912
913
	struct fujitsu_hotkey_t *fujitsu_hotkey = acpi_driver_data(device);
	struct input_dev *input = fujitsu_hotkey->input;
Jonathan Woithe's avatar
Jonathan Woithe committed
914

915
#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
916
917
	if (fujitsu_hotkey->logolamp_registered)
		led_classdev_unregister(&logolamp_led);
Jonathan Woithe's avatar
Jonathan Woithe committed
918

919
920
921
	if (fujitsu_hotkey->kblamps_registered)
		led_classdev_unregister(&kblamps_led);
#endif
Jonathan Woithe's avatar
Jonathan Woithe committed
922

923
	input_unregister_device(input);
Jonathan Woithe's avatar
Jonathan Woithe committed
924

925
	kfifo_free(&fujitsu_hotkey->fifo);
Jonathan Woithe's avatar
Jonathan Woithe committed
926

927
928
	fujitsu_hotkey->acpi_handle = NULL;

Jonathan Woithe's avatar
Jonathan Woithe committed
929
930
931
	return 0;
}

932
static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event)
Jonathan Woithe's avatar
Jonathan Woithe committed
933
934
935
936
937
938
939
940
{
	struct input_dev *input;
	int keycode, keycode_r;
	unsigned int irb = 1;
	int i, status;

	input = fujitsu_hotkey->input;

941
942
943
	if (fujitsu_hotkey->rfkill_supported)
		fujitsu_hotkey->rfkill_state =
			call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0);
Jonathan Woithe's avatar
Jonathan Woithe committed
944
945
946
947

	switch (event) {
	case ACPI_FUJITSU_NOTIFY_CODE1:
		i = 0;
948
949
950
		while ((irb =
			call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0
				&& (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) {
Jonathan Woithe's avatar
Jonathan Woithe committed
951
			switch (irb & 0x4ff) {
952
953
			case KEY1_CODE:
				keycode = fujitsu->keycode1;
Jonathan Woithe's avatar
Jonathan Woithe committed
954
				break;
955
956
			case KEY2_CODE:
				keycode = fujitsu->keycode2;
Jonathan Woithe's avatar
Jonathan Woithe committed
957
				break;
958
959
			case KEY3_CODE:
				keycode = fujitsu->keycode3;
Jonathan Woithe's avatar
Jonathan Woithe committed
960
				break;
961
962
			case KEY4_CODE:
				keycode = fujitsu->keycode4;
Jonathan Woithe's avatar
Jonathan Woithe committed
963
964
965
966
967
968
				break;
			case 0:
				keycode = 0;
				break;
			default:
				vdbg_printk(FUJLAPTOP_DBG_WARN,
969
					    "Unknown GIRB result [%x]\n", irb);
Jonathan Woithe's avatar
Jonathan Woithe committed
970
971
972
973
974
975
976
				keycode = -1;
				break;
			}
			if (keycode > 0) {
				vdbg_printk(FUJLAPTOP_DBG_TRACE,
					"Push keycode into ringbuffer [%d]\n",
					keycode);
977
				status = kfifo_in_locked(&fujitsu_hotkey->fifo,
978
						   (unsigned char *)&keycode,
Stefani Seibold's avatar
Stefani Seibold committed
979
980
						   sizeof(keycode),
						   &fujitsu_hotkey->fifo_lock);
Jonathan Woithe's avatar
Jonathan Woithe committed
981
982
				if (status != sizeof(keycode)) {
					vdbg_printk(FUJLAPTOP_DBG_WARN,
983
984
					    "Could not push keycode [0x%x]\n",
					    keycode);
Jonathan Woithe's avatar
Jonathan Woithe committed
985
986
987
988
989
990
				} else {
					input_report_key(input, keycode, 1);
					input_sync(input);
				}
			} else if (keycode == 0) {
				while ((status =
991
					kfifo_out_locked(
Stefani Seibold's avatar
Stefani Seibold committed
992
993
994
995
996
					 &fujitsu_hotkey->fifo,
					 (unsigned char *) &keycode_r,
					 sizeof(keycode_r),
					 &fujitsu_hotkey->fifo_lock))
					 == sizeof(keycode_r)) {
Jonathan Woithe's avatar
Jonathan Woithe committed
997
998
999
					input_report_key(input, keycode_r, 0);
					input_sync(input);
					vdbg_printk(FUJLAPTOP_DBG_TRACE,
1000
					  "Pop keycode from ringbuffer [%d]\n",
For faster browsing, not all history is shown. View entire blame