diff options
Diffstat (limited to 'drivers/platform/x86/sony-laptop.c')
-rw-r--r-- | drivers/platform/x86/sony-laptop.c | 100 |
1 files changed, 53 insertions, 47 deletions
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 3f71a605a492..1387c5f9c24d 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c | |||
@@ -58,6 +58,7 @@ | |||
58 | #include <linux/kfifo.h> | 58 | #include <linux/kfifo.h> |
59 | #include <linux/workqueue.h> | 59 | #include <linux/workqueue.h> |
60 | #include <linux/acpi.h> | 60 | #include <linux/acpi.h> |
61 | #include <linux/slab.h> | ||
61 | #include <acpi/acpi_drivers.h> | 62 | #include <acpi/acpi_drivers.h> |
62 | #include <acpi/acpi_bus.h> | 63 | #include <acpi/acpi_bus.h> |
63 | #include <asm/uaccess.h> | 64 | #include <asm/uaccess.h> |
@@ -145,7 +146,7 @@ struct sony_laptop_input_s { | |||
145 | struct input_dev *key_dev; | 146 | struct input_dev *key_dev; |
146 | struct kfifo fifo; | 147 | struct kfifo fifo; |
147 | spinlock_t fifo_lock; | 148 | spinlock_t fifo_lock; |
148 | struct workqueue_struct *wq; | 149 | struct timer_list release_key_timer; |
149 | }; | 150 | }; |
150 | 151 | ||
151 | static struct sony_laptop_input_s sony_laptop_input = { | 152 | static struct sony_laptop_input_s sony_laptop_input = { |
@@ -299,20 +300,26 @@ static int sony_laptop_input_keycode_map[] = { | |||
299 | }; | 300 | }; |
300 | 301 | ||
301 | /* release buttons after a short delay if pressed */ | 302 | /* release buttons after a short delay if pressed */ |
302 | static void do_sony_laptop_release_key(struct work_struct *work) | 303 | static void do_sony_laptop_release_key(unsigned long unused) |
303 | { | 304 | { |
304 | struct sony_laptop_keypress kp; | 305 | struct sony_laptop_keypress kp; |
306 | unsigned long flags; | ||
307 | |||
308 | spin_lock_irqsave(&sony_laptop_input.fifo_lock, flags); | ||
305 | 309 | ||
306 | while (kfifo_out_locked(&sony_laptop_input.fifo, (unsigned char *)&kp, | 310 | if (kfifo_out(&sony_laptop_input.fifo, |
307 | sizeof(kp), &sony_laptop_input.fifo_lock) | 311 | (unsigned char *)&kp, sizeof(kp)) == sizeof(kp)) { |
308 | == sizeof(kp)) { | ||
309 | msleep(10); | ||
310 | input_report_key(kp.dev, kp.key, 0); | 312 | input_report_key(kp.dev, kp.key, 0); |
311 | input_sync(kp.dev); | 313 | input_sync(kp.dev); |
312 | } | 314 | } |
315 | |||
316 | /* If there is something in the fifo schedule next release. */ | ||
317 | if (kfifo_len(&sony_laptop_input.fifo) != 0) | ||
318 | mod_timer(&sony_laptop_input.release_key_timer, | ||
319 | jiffies + msecs_to_jiffies(10)); | ||
320 | |||
321 | spin_unlock_irqrestore(&sony_laptop_input.fifo_lock, flags); | ||
313 | } | 322 | } |
314 | static DECLARE_WORK(sony_laptop_release_key_work, | ||
315 | do_sony_laptop_release_key); | ||
316 | 323 | ||
317 | /* forward event to the input subsystem */ | 324 | /* forward event to the input subsystem */ |
318 | static void sony_laptop_report_input_event(u8 event) | 325 | static void sony_laptop_report_input_event(u8 event) |
@@ -366,13 +373,13 @@ static void sony_laptop_report_input_event(u8 event) | |||
366 | /* we emit the scancode so we can always remap the key */ | 373 | /* we emit the scancode so we can always remap the key */ |
367 | input_event(kp.dev, EV_MSC, MSC_SCAN, event); | 374 | input_event(kp.dev, EV_MSC, MSC_SCAN, event); |
368 | input_sync(kp.dev); | 375 | input_sync(kp.dev); |
369 | kfifo_in_locked(&sony_laptop_input.fifo, | ||
370 | (unsigned char *)&kp, sizeof(kp), | ||
371 | &sony_laptop_input.fifo_lock); | ||
372 | 376 | ||
373 | if (!work_pending(&sony_laptop_release_key_work)) | 377 | /* schedule key release */ |
374 | queue_work(sony_laptop_input.wq, | 378 | kfifo_in_locked(&sony_laptop_input.fifo, |
375 | &sony_laptop_release_key_work); | 379 | (unsigned char *)&kp, sizeof(kp), |
380 | &sony_laptop_input.fifo_lock); | ||
381 | mod_timer(&sony_laptop_input.release_key_timer, | ||
382 | jiffies + msecs_to_jiffies(10)); | ||
376 | } else | 383 | } else |
377 | dprintk("unknown input event %.2x\n", event); | 384 | dprintk("unknown input event %.2x\n", event); |
378 | } | 385 | } |
@@ -390,27 +397,21 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device) | |||
390 | 397 | ||
391 | /* kfifo */ | 398 | /* kfifo */ |
392 | spin_lock_init(&sony_laptop_input.fifo_lock); | 399 | spin_lock_init(&sony_laptop_input.fifo_lock); |
393 | error = | 400 | error = kfifo_alloc(&sony_laptop_input.fifo, |
394 | kfifo_alloc(&sony_laptop_input.fifo, SONY_LAPTOP_BUF_SIZE, GFP_KERNEL); | 401 | SONY_LAPTOP_BUF_SIZE, GFP_KERNEL); |
395 | if (error) { | 402 | if (error) { |
396 | printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n"); | 403 | printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n"); |
397 | goto err_dec_users; | 404 | goto err_dec_users; |
398 | } | 405 | } |
399 | 406 | ||
400 | /* init workqueue */ | 407 | setup_timer(&sony_laptop_input.release_key_timer, |
401 | sony_laptop_input.wq = create_singlethread_workqueue("sony-laptop"); | 408 | do_sony_laptop_release_key, 0); |
402 | if (!sony_laptop_input.wq) { | ||
403 | printk(KERN_ERR DRV_PFX | ||
404 | "Unable to create workqueue.\n"); | ||
405 | error = -ENXIO; | ||
406 | goto err_free_kfifo; | ||
407 | } | ||
408 | 409 | ||
409 | /* input keys */ | 410 | /* input keys */ |
410 | key_dev = input_allocate_device(); | 411 | key_dev = input_allocate_device(); |
411 | if (!key_dev) { | 412 | if (!key_dev) { |
412 | error = -ENOMEM; | 413 | error = -ENOMEM; |
413 | goto err_destroy_wq; | 414 | goto err_free_kfifo; |
414 | } | 415 | } |
415 | 416 | ||
416 | key_dev->name = "Sony Vaio Keys"; | 417 | key_dev->name = "Sony Vaio Keys"; |
@@ -419,18 +420,15 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device) | |||
419 | key_dev->dev.parent = &acpi_device->dev; | 420 | key_dev->dev.parent = &acpi_device->dev; |
420 | 421 | ||
421 | /* Initialize the Input Drivers: special keys */ | 422 | /* Initialize the Input Drivers: special keys */ |
422 | set_bit(EV_KEY, key_dev->evbit); | 423 | input_set_capability(key_dev, EV_MSC, MSC_SCAN); |
423 | set_bit(EV_MSC, key_dev->evbit); | 424 | |
424 | set_bit(MSC_SCAN, key_dev->mscbit); | 425 | __set_bit(EV_KEY, key_dev->evbit); |
425 | key_dev->keycodesize = sizeof(sony_laptop_input_keycode_map[0]); | 426 | key_dev->keycodesize = sizeof(sony_laptop_input_keycode_map[0]); |
426 | key_dev->keycodemax = ARRAY_SIZE(sony_laptop_input_keycode_map); | 427 | key_dev->keycodemax = ARRAY_SIZE(sony_laptop_input_keycode_map); |
427 | key_dev->keycode = &sony_laptop_input_keycode_map; | 428 | key_dev->keycode = &sony_laptop_input_keycode_map; |
428 | for (i = 0; i < ARRAY_SIZE(sony_laptop_input_keycode_map); i++) { | 429 | for (i = 0; i < ARRAY_SIZE(sony_laptop_input_keycode_map); i++) |
429 | if (sony_laptop_input_keycode_map[i] != KEY_RESERVED) { | 430 | __set_bit(sony_laptop_input_keycode_map[i], key_dev->keybit); |
430 | set_bit(sony_laptop_input_keycode_map[i], | 431 | __clear_bit(KEY_RESERVED, key_dev->keybit); |
431 | key_dev->keybit); | ||
432 | } | ||
433 | } | ||
434 | 432 | ||
435 | error = input_register_device(key_dev); | 433 | error = input_register_device(key_dev); |
436 | if (error) | 434 | if (error) |
@@ -450,9 +448,8 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device) | |||
450 | jog_dev->id.vendor = PCI_VENDOR_ID_SONY; | 448 | jog_dev->id.vendor = PCI_VENDOR_ID_SONY; |
451 | key_dev->dev.parent = &acpi_device->dev; | 449 | key_dev->dev.parent = &acpi_device->dev; |
452 | 450 | ||
453 | jog_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); | 451 | input_set_capability(jog_dev, EV_KEY, BTN_MIDDLE); |
454 | jog_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_MIDDLE); | 452 | input_set_capability(jog_dev, EV_REL, REL_WHEEL); |
455 | jog_dev->relbit[0] = BIT_MASK(REL_WHEEL); | ||
456 | 453 | ||
457 | error = input_register_device(jog_dev); | 454 | error = input_register_device(jog_dev); |
458 | if (error) | 455 | if (error) |
@@ -473,9 +470,6 @@ err_unregister_keydev: | |||
473 | err_free_keydev: | 470 | err_free_keydev: |
474 | input_free_device(key_dev); | 471 | input_free_device(key_dev); |
475 | 472 | ||
476 | err_destroy_wq: | ||
477 | destroy_workqueue(sony_laptop_input.wq); | ||
478 | |||
479 | err_free_kfifo: | 473 | err_free_kfifo: |
480 | kfifo_free(&sony_laptop_input.fifo); | 474 | kfifo_free(&sony_laptop_input.fifo); |
481 | 475 | ||
@@ -486,12 +480,23 @@ err_dec_users: | |||
486 | 480 | ||
487 | static void sony_laptop_remove_input(void) | 481 | static void sony_laptop_remove_input(void) |
488 | { | 482 | { |
489 | /* cleanup only after the last user has gone */ | 483 | struct sony_laptop_keypress kp = { NULL }; |
484 | |||
485 | /* Cleanup only after the last user has gone */ | ||
490 | if (!atomic_dec_and_test(&sony_laptop_input.users)) | 486 | if (!atomic_dec_and_test(&sony_laptop_input.users)) |
491 | return; | 487 | return; |
492 | 488 | ||
493 | /* flush workqueue first */ | 489 | del_timer_sync(&sony_laptop_input.release_key_timer); |
494 | flush_workqueue(sony_laptop_input.wq); | 490 | |
491 | /* | ||
492 | * Generate key-up events for remaining keys. Note that we don't | ||
493 | * need locking since nobody is adding new events to the kfifo. | ||
494 | */ | ||
495 | while (kfifo_out(&sony_laptop_input.fifo, | ||
496 | (unsigned char *)&kp, sizeof(kp)) == sizeof(kp)) { | ||
497 | input_report_key(kp.dev, kp.key, 0); | ||
498 | input_sync(kp.dev); | ||
499 | } | ||
495 | 500 | ||
496 | /* destroy input devs */ | 501 | /* destroy input devs */ |
497 | input_unregister_device(sony_laptop_input.key_dev); | 502 | input_unregister_device(sony_laptop_input.key_dev); |
@@ -502,7 +507,6 @@ static void sony_laptop_remove_input(void) | |||
502 | sony_laptop_input.jog_dev = NULL; | 507 | sony_laptop_input.jog_dev = NULL; |
503 | } | 508 | } |
504 | 509 | ||
505 | destroy_workqueue(sony_laptop_input.wq); | ||
506 | kfifo_free(&sony_laptop_input.fifo); | 510 | kfifo_free(&sony_laptop_input.fifo); |
507 | } | 511 | } |
508 | 512 | ||
@@ -1288,9 +1292,13 @@ static int sony_nc_add(struct acpi_device *device) | |||
1288 | "controlled by ACPI video driver\n"); | 1292 | "controlled by ACPI video driver\n"); |
1289 | } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", | 1293 | } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", |
1290 | &handle))) { | 1294 | &handle))) { |
1295 | struct backlight_properties props; | ||
1296 | memset(&props, 0, sizeof(struct backlight_properties)); | ||
1297 | props.max_brightness = SONY_MAX_BRIGHTNESS - 1; | ||
1291 | sony_backlight_device = backlight_device_register("sony", NULL, | 1298 | sony_backlight_device = backlight_device_register("sony", NULL, |
1292 | NULL, | 1299 | NULL, |
1293 | &sony_backlight_ops); | 1300 | &sony_backlight_ops, |
1301 | &props); | ||
1294 | 1302 | ||
1295 | if (IS_ERR(sony_backlight_device)) { | 1303 | if (IS_ERR(sony_backlight_device)) { |
1296 | printk(KERN_WARNING DRV_PFX "unable to register backlight device\n"); | 1304 | printk(KERN_WARNING DRV_PFX "unable to register backlight device\n"); |
@@ -1299,8 +1307,6 @@ static int sony_nc_add(struct acpi_device *device) | |||
1299 | sony_backlight_device->props.brightness = | 1307 | sony_backlight_device->props.brightness = |
1300 | sony_backlight_get_brightness | 1308 | sony_backlight_get_brightness |
1301 | (sony_backlight_device); | 1309 | (sony_backlight_device); |
1302 | sony_backlight_device->props.max_brightness = | ||
1303 | SONY_MAX_BRIGHTNESS - 1; | ||
1304 | } | 1310 | } |
1305 | 1311 | ||
1306 | } | 1312 | } |