11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * PC Speaker beeper driver for Linux 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (c) 2002 Vojtech Pavlik 51da177e4SLinus Torvalds * Copyright (c) 1992 Orest Zborowski 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds */ 81da177e4SLinus Torvalds 91da177e4SLinus Torvalds /* 101da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify it 111da177e4SLinus Torvalds * under the terms of the GNU General Public License version 2 as published by 121da177e4SLinus Torvalds * the Free Software Foundation 131da177e4SLinus Torvalds */ 141da177e4SLinus Torvalds 151da177e4SLinus Torvalds #include <linux/kernel.h> 161da177e4SLinus Torvalds #include <linux/module.h> 171da177e4SLinus Torvalds #include <linux/init.h> 181da177e4SLinus Torvalds #include <linux/input.h> 1959317747SDmitry Torokhov #include <linux/platform_device.h> 201da177e4SLinus Torvalds #include <asm/8253pit.h> 211da177e4SLinus Torvalds #include <asm/io.h> 221da177e4SLinus Torvalds 231da177e4SLinus Torvalds MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); 241da177e4SLinus Torvalds MODULE_DESCRIPTION("PC Speaker beeper driver"); 251da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 261da177e4SLinus Torvalds 2759317747SDmitry Torokhov static struct platform_device *pcspkr_platform_device; 281da177e4SLinus Torvalds static DEFINE_SPINLOCK(i8253_beep_lock); 291da177e4SLinus Torvalds 301da177e4SLinus Torvalds static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) 311da177e4SLinus Torvalds { 321da177e4SLinus Torvalds unsigned int count = 0; 331da177e4SLinus Torvalds unsigned long flags; 341da177e4SLinus Torvalds 351da177e4SLinus Torvalds if (type != EV_SND) 361da177e4SLinus Torvalds return -1; 371da177e4SLinus Torvalds 381da177e4SLinus Torvalds switch (code) { 391da177e4SLinus Torvalds case SND_BELL: if (value) value = 1000; 401da177e4SLinus Torvalds case SND_TONE: break; 411da177e4SLinus Torvalds default: return -1; 421da177e4SLinus Torvalds } 431da177e4SLinus Torvalds 441da177e4SLinus Torvalds if (value > 20 && value < 32767) 451da177e4SLinus Torvalds count = PIT_TICK_RATE / value; 461da177e4SLinus Torvalds 471da177e4SLinus Torvalds spin_lock_irqsave(&i8253_beep_lock, flags); 481da177e4SLinus Torvalds 491da177e4SLinus Torvalds if (count) { 501da177e4SLinus Torvalds /* enable counter 2 */ 511da177e4SLinus Torvalds outb_p(inb_p(0x61) | 3, 0x61); 521da177e4SLinus Torvalds /* set command for counter 2, 2 byte write */ 531da177e4SLinus Torvalds outb_p(0xB6, 0x43); 541da177e4SLinus Torvalds /* select desired HZ */ 551da177e4SLinus Torvalds outb_p(count & 0xff, 0x42); 561da177e4SLinus Torvalds outb((count >> 8) & 0xff, 0x42); 571da177e4SLinus Torvalds } else { 581da177e4SLinus Torvalds /* disable counter 2 */ 591da177e4SLinus Torvalds outb(inb_p(0x61) & 0xFC, 0x61); 601da177e4SLinus Torvalds } 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds spin_unlock_irqrestore(&i8253_beep_lock, flags); 631da177e4SLinus Torvalds 641da177e4SLinus Torvalds return 0; 651da177e4SLinus Torvalds } 661da177e4SLinus Torvalds 6759317747SDmitry Torokhov static int __devinit pcspkr_probe(struct platform_device *dev) 681da177e4SLinus Torvalds { 6959317747SDmitry Torokhov struct input_dev *pcspkr_dev; 7059317747SDmitry Torokhov int err; 7159317747SDmitry Torokhov 7276b7cddfSDmitry Torokhov pcspkr_dev = input_allocate_device(); 7376b7cddfSDmitry Torokhov if (!pcspkr_dev) 7476b7cddfSDmitry Torokhov return -ENOMEM; 751da177e4SLinus Torvalds 7676b7cddfSDmitry Torokhov pcspkr_dev->name = "PC Speaker"; 771259f2b3SDmitry Torokhov pcspkr_dev->phys = "isa0061/input0"; 7876b7cddfSDmitry Torokhov pcspkr_dev->id.bustype = BUS_ISA; 7976b7cddfSDmitry Torokhov pcspkr_dev->id.vendor = 0x001f; 8076b7cddfSDmitry Torokhov pcspkr_dev->id.product = 0x0001; 8176b7cddfSDmitry Torokhov pcspkr_dev->id.version = 0x0100; 8259317747SDmitry Torokhov pcspkr_dev->cdev.dev = &dev->dev; 831da177e4SLinus Torvalds 8476b7cddfSDmitry Torokhov pcspkr_dev->evbit[0] = BIT(EV_SND); 8576b7cddfSDmitry Torokhov pcspkr_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE); 8676b7cddfSDmitry Torokhov pcspkr_dev->event = pcspkr_event; 871da177e4SLinus Torvalds 8859317747SDmitry Torokhov err = input_register_device(pcspkr_dev); 8959317747SDmitry Torokhov if (err) { 9059317747SDmitry Torokhov input_free_device(pcspkr_dev); 9159317747SDmitry Torokhov return err; 9259317747SDmitry Torokhov } 9359317747SDmitry Torokhov 9459317747SDmitry Torokhov platform_set_drvdata(dev, pcspkr_dev); 951da177e4SLinus Torvalds 961da177e4SLinus Torvalds return 0; 971da177e4SLinus Torvalds } 981da177e4SLinus Torvalds 9959317747SDmitry Torokhov static int __devexit pcspkr_remove(struct platform_device *dev) 1001da177e4SLinus Torvalds { 10159317747SDmitry Torokhov struct input_dev *pcspkr_dev = platform_get_drvdata(dev); 10259317747SDmitry Torokhov 10376b7cddfSDmitry Torokhov input_unregister_device(pcspkr_dev); 10459317747SDmitry Torokhov platform_set_drvdata(dev, NULL); 1051da177e4SLinus Torvalds /* turn off the speaker */ 1061da177e4SLinus Torvalds pcspkr_event(NULL, EV_SND, SND_BELL, 0); 10759317747SDmitry Torokhov 10859317747SDmitry Torokhov return 0; 10959317747SDmitry Torokhov } 11059317747SDmitry Torokhov 11159317747SDmitry Torokhov static int pcspkr_suspend(struct platform_device *dev, pm_message_t state) 11259317747SDmitry Torokhov { 11359317747SDmitry Torokhov pcspkr_event(NULL, EV_SND, SND_BELL, 0); 11459317747SDmitry Torokhov 11559317747SDmitry Torokhov return 0; 11659317747SDmitry Torokhov } 11759317747SDmitry Torokhov 11859317747SDmitry Torokhov static void pcspkr_shutdown(struct platform_device *dev) 11959317747SDmitry Torokhov { 12059317747SDmitry Torokhov /* turn off the speaker */ 12159317747SDmitry Torokhov pcspkr_event(NULL, EV_SND, SND_BELL, 0); 12259317747SDmitry Torokhov } 12359317747SDmitry Torokhov 12459317747SDmitry Torokhov static struct platform_driver pcspkr_platform_driver = { 12559317747SDmitry Torokhov .driver = { 12659317747SDmitry Torokhov .name = "pcspkr", 12759317747SDmitry Torokhov .owner = THIS_MODULE, 12859317747SDmitry Torokhov }, 12959317747SDmitry Torokhov .probe = pcspkr_probe, 13059317747SDmitry Torokhov .remove = __devexit_p(pcspkr_remove), 13159317747SDmitry Torokhov .suspend = pcspkr_suspend, 13259317747SDmitry Torokhov .shutdown = pcspkr_shutdown, 13359317747SDmitry Torokhov }; 13459317747SDmitry Torokhov 13559317747SDmitry Torokhov 13659317747SDmitry Torokhov static int __init pcspkr_init(void) 13759317747SDmitry Torokhov { 13859317747SDmitry Torokhov int err; 13959317747SDmitry Torokhov 14059317747SDmitry Torokhov err = platform_driver_register(&pcspkr_platform_driver); 14159317747SDmitry Torokhov if (err) 14259317747SDmitry Torokhov return err; 14359317747SDmitry Torokhov 14459317747SDmitry Torokhov pcspkr_platform_device = platform_device_alloc("pcspkr", -1); 14559317747SDmitry Torokhov if (!pcspkr_platform_device) { 14659317747SDmitry Torokhov err = -ENOMEM; 14759317747SDmitry Torokhov goto err_unregister_driver; 14859317747SDmitry Torokhov } 14959317747SDmitry Torokhov 15059317747SDmitry Torokhov err = platform_device_add(pcspkr_platform_device); 15159317747SDmitry Torokhov if (err) 15259317747SDmitry Torokhov goto err_free_device; 15359317747SDmitry Torokhov 15459317747SDmitry Torokhov return 0; 15559317747SDmitry Torokhov 15659317747SDmitry Torokhov err_free_device: 15759317747SDmitry Torokhov platform_device_put(pcspkr_platform_device); 15859317747SDmitry Torokhov err_unregister_driver: 15959317747SDmitry Torokhov platform_driver_unregister(&pcspkr_platform_driver); 16059317747SDmitry Torokhov 16159317747SDmitry Torokhov return err; 16259317747SDmitry Torokhov } 16359317747SDmitry Torokhov 16459317747SDmitry Torokhov static void __exit pcspkr_exit(void) 16559317747SDmitry Torokhov { 16659317747SDmitry Torokhov platform_device_unregister(pcspkr_platform_device); 16759317747SDmitry Torokhov platform_driver_unregister(&pcspkr_platform_driver); 1681da177e4SLinus Torvalds } 1691da177e4SLinus Torvalds 1701da177e4SLinus Torvalds module_init(pcspkr_init); 1711da177e4SLinus Torvalds module_exit(pcspkr_exit); 172