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 271da177e4SLinus Torvalds static DEFINE_SPINLOCK(i8253_beep_lock); 281da177e4SLinus Torvalds 291da177e4SLinus Torvalds static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) 301da177e4SLinus Torvalds { 311da177e4SLinus Torvalds unsigned int count = 0; 321da177e4SLinus Torvalds unsigned long flags; 331da177e4SLinus Torvalds 341da177e4SLinus Torvalds if (type != EV_SND) 351da177e4SLinus Torvalds return -1; 361da177e4SLinus Torvalds 371da177e4SLinus Torvalds switch (code) { 381da177e4SLinus Torvalds case SND_BELL: if (value) value = 1000; 391da177e4SLinus Torvalds case SND_TONE: break; 401da177e4SLinus Torvalds default: return -1; 411da177e4SLinus Torvalds } 421da177e4SLinus Torvalds 431da177e4SLinus Torvalds if (value > 20 && value < 32767) 441da177e4SLinus Torvalds count = PIT_TICK_RATE / value; 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds spin_lock_irqsave(&i8253_beep_lock, flags); 471da177e4SLinus Torvalds 481da177e4SLinus Torvalds if (count) { 491da177e4SLinus Torvalds /* enable counter 2 */ 501da177e4SLinus Torvalds outb_p(inb_p(0x61) | 3, 0x61); 511da177e4SLinus Torvalds /* set command for counter 2, 2 byte write */ 521da177e4SLinus Torvalds outb_p(0xB6, 0x43); 531da177e4SLinus Torvalds /* select desired HZ */ 541da177e4SLinus Torvalds outb_p(count & 0xff, 0x42); 551da177e4SLinus Torvalds outb((count >> 8) & 0xff, 0x42); 561da177e4SLinus Torvalds } else { 571da177e4SLinus Torvalds /* disable counter 2 */ 581da177e4SLinus Torvalds outb(inb_p(0x61) & 0xFC, 0x61); 591da177e4SLinus Torvalds } 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds spin_unlock_irqrestore(&i8253_beep_lock, flags); 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds return 0; 641da177e4SLinus Torvalds } 651da177e4SLinus Torvalds 6659317747SDmitry Torokhov static int __devinit pcspkr_probe(struct platform_device *dev) 671da177e4SLinus Torvalds { 6859317747SDmitry Torokhov struct input_dev *pcspkr_dev; 6959317747SDmitry Torokhov int err; 7059317747SDmitry Torokhov 7176b7cddfSDmitry Torokhov pcspkr_dev = input_allocate_device(); 7276b7cddfSDmitry Torokhov if (!pcspkr_dev) 7376b7cddfSDmitry Torokhov return -ENOMEM; 741da177e4SLinus Torvalds 7576b7cddfSDmitry Torokhov pcspkr_dev->name = "PC Speaker"; 761259f2b3SDmitry Torokhov pcspkr_dev->phys = "isa0061/input0"; 7776b7cddfSDmitry Torokhov pcspkr_dev->id.bustype = BUS_ISA; 7876b7cddfSDmitry Torokhov pcspkr_dev->id.vendor = 0x001f; 7976b7cddfSDmitry Torokhov pcspkr_dev->id.product = 0x0001; 8076b7cddfSDmitry Torokhov pcspkr_dev->id.version = 0x0100; 8159317747SDmitry Torokhov pcspkr_dev->cdev.dev = &dev->dev; 821da177e4SLinus Torvalds 8376b7cddfSDmitry Torokhov pcspkr_dev->evbit[0] = BIT(EV_SND); 8476b7cddfSDmitry Torokhov pcspkr_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE); 8576b7cddfSDmitry Torokhov pcspkr_dev->event = pcspkr_event; 861da177e4SLinus Torvalds 8759317747SDmitry Torokhov err = input_register_device(pcspkr_dev); 8859317747SDmitry Torokhov if (err) { 8959317747SDmitry Torokhov input_free_device(pcspkr_dev); 9059317747SDmitry Torokhov return err; 9159317747SDmitry Torokhov } 9259317747SDmitry Torokhov 9359317747SDmitry Torokhov platform_set_drvdata(dev, pcspkr_dev); 941da177e4SLinus Torvalds 951da177e4SLinus Torvalds return 0; 961da177e4SLinus Torvalds } 971da177e4SLinus Torvalds 9859317747SDmitry Torokhov static int __devexit pcspkr_remove(struct platform_device *dev) 991da177e4SLinus Torvalds { 10059317747SDmitry Torokhov struct input_dev *pcspkr_dev = platform_get_drvdata(dev); 10159317747SDmitry Torokhov 10276b7cddfSDmitry Torokhov input_unregister_device(pcspkr_dev); 10359317747SDmitry Torokhov platform_set_drvdata(dev, NULL); 1041da177e4SLinus Torvalds /* turn off the speaker */ 1051da177e4SLinus Torvalds pcspkr_event(NULL, EV_SND, SND_BELL, 0); 10659317747SDmitry Torokhov 10759317747SDmitry Torokhov return 0; 10859317747SDmitry Torokhov } 10959317747SDmitry Torokhov 11059317747SDmitry Torokhov static int pcspkr_suspend(struct platform_device *dev, pm_message_t state) 11159317747SDmitry Torokhov { 11259317747SDmitry Torokhov pcspkr_event(NULL, EV_SND, SND_BELL, 0); 11359317747SDmitry Torokhov 11459317747SDmitry Torokhov return 0; 11559317747SDmitry Torokhov } 11659317747SDmitry Torokhov 11759317747SDmitry Torokhov static void pcspkr_shutdown(struct platform_device *dev) 11859317747SDmitry Torokhov { 11959317747SDmitry Torokhov /* turn off the speaker */ 12059317747SDmitry Torokhov pcspkr_event(NULL, EV_SND, SND_BELL, 0); 12159317747SDmitry Torokhov } 12259317747SDmitry Torokhov 12359317747SDmitry Torokhov static struct platform_driver pcspkr_platform_driver = { 12459317747SDmitry Torokhov .driver = { 12559317747SDmitry Torokhov .name = "pcspkr", 12659317747SDmitry Torokhov .owner = THIS_MODULE, 12759317747SDmitry Torokhov }, 12859317747SDmitry Torokhov .probe = pcspkr_probe, 12959317747SDmitry Torokhov .remove = __devexit_p(pcspkr_remove), 13059317747SDmitry Torokhov .suspend = pcspkr_suspend, 13159317747SDmitry Torokhov .shutdown = pcspkr_shutdown, 13259317747SDmitry Torokhov }; 13359317747SDmitry Torokhov 13459317747SDmitry Torokhov 13559317747SDmitry Torokhov static int __init pcspkr_init(void) 13659317747SDmitry Torokhov { 137*e5c6c8e4SMichael Neuling return platform_driver_register(&pcspkr_platform_driver); 13859317747SDmitry Torokhov } 13959317747SDmitry Torokhov 14059317747SDmitry Torokhov static void __exit pcspkr_exit(void) 14159317747SDmitry Torokhov { 14259317747SDmitry Torokhov platform_driver_unregister(&pcspkr_platform_driver); 1431da177e4SLinus Torvalds } 1441da177e4SLinus Torvalds 1451da177e4SLinus Torvalds module_init(pcspkr_init); 1461da177e4SLinus Torvalds module_exit(pcspkr_exit); 147