blob: 5778220a18d23945eeb7f3cdb1f054cef1a308bc [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Driver for PC-speaker like devices found on various Sparc systems.
3 *
4 * Copyright (c) 2002 Vojtech Pavlik
5 * Copyright (c) 2002 David S. Miller (davem@redhat.com)
6 */
7#include <linux/config.h>
8#include <linux/kernel.h>
9#include <linux/module.h>
10#include <linux/init.h>
11#include <linux/input.h>
12
13#include <asm/io.h>
14#include <asm/ebus.h>
15#ifdef CONFIG_SPARC64
16#include <asm/isa.h>
17#endif
18
19MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
Dmitry Torokhov76b7cdd2005-09-15 02:01:51 -050020MODULE_DESCRIPTION("Sparc Speaker beeper driver");
Linus Torvalds1da177e2005-04-16 15:20:36 -070021MODULE_LICENSE("GPL");
22
23static unsigned long beep_iobase;
Dmitry Torokhov76b7cdd2005-09-15 02:01:51 -050024static struct input_dev *sparcspkr_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -070025
26DEFINE_SPINLOCK(beep_lock);
27
28static void __init init_sparcspkr_struct(void)
29{
Dmitry Torokhov76b7cdd2005-09-15 02:01:51 -050030 sparcspkr_dev->evbit[0] = BIT(EV_SND);
31 sparcspkr_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -070032
Dmitry Torokhov76b7cdd2005-09-15 02:01:51 -050033 sparcspkr_dev->phys = "sparc/input0";
34 sparcspkr_dev->id.bustype = BUS_ISA;
35 sparcspkr_dev->id.vendor = 0x001f;
36 sparcspkr_dev->id.product = 0x0001;
37 sparcspkr_dev->id.version = 0x0100;
Linus Torvalds1da177e2005-04-16 15:20:36 -070038}
39
40static int ebus_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
41{
42 unsigned int count = 0;
43 unsigned long flags;
44
45 if (type != EV_SND)
46 return -1;
47
48 switch (code) {
49 case SND_BELL: if (value) value = 1000;
50 case SND_TONE: break;
51 default: return -1;
52 }
53
54 if (value > 20 && value < 32767)
55 count = 1193182 / value;
56
57 spin_lock_irqsave(&beep_lock, flags);
58
59 /* EBUS speaker only has on/off state, the frequency does not
60 * appear to be programmable.
61 */
62 if (count) {
63 if (beep_iobase & 0x2UL)
64 outb(1, beep_iobase);
65 else
66 outl(1, beep_iobase);
67 } else {
68 if (beep_iobase & 0x2UL)
69 outb(0, beep_iobase);
70 else
71 outl(0, beep_iobase);
72 }
73
74 spin_unlock_irqrestore(&beep_lock, flags);
75
76 return 0;
77}
78
79static int __init init_ebus_beep(struct linux_ebus_device *edev)
80{
81 beep_iobase = edev->resource[0].start;
82
Dmitry Torokhov76b7cdd2005-09-15 02:01:51 -050083 sparcspkr_dev = input_allocate_device();
84 if (!sparcspkr_dev)
85 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -070086
Dmitry Torokhov76b7cdd2005-09-15 02:01:51 -050087 sparcspkr_dev->name = "Sparc EBUS Speaker";
88 sparcspkr_dev->event = ebus_spkr_event;
Linus Torvalds1da177e2005-04-16 15:20:36 -070089
Dmitry Torokhov76b7cdd2005-09-15 02:01:51 -050090 input_register_device(sparcspkr_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -070091
Linus Torvalds1da177e2005-04-16 15:20:36 -070092 return 0;
93}
94
95#ifdef CONFIG_SPARC64
96static int isa_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
97{
98 unsigned int count = 0;
99 unsigned long flags;
100
101 if (type != EV_SND)
102 return -1;
103
104 switch (code) {
105 case SND_BELL: if (value) value = 1000;
106 case SND_TONE: break;
107 default: return -1;
108 }
109
110 if (value > 20 && value < 32767)
111 count = 1193182 / value;
112
113 spin_lock_irqsave(&beep_lock, flags);
114
115 if (count) {
116 /* enable counter 2 */
117 outb(inb(beep_iobase + 0x61) | 3, beep_iobase + 0x61);
118 /* set command for counter 2, 2 byte write */
119 outb(0xB6, beep_iobase + 0x43);
120 /* select desired HZ */
121 outb(count & 0xff, beep_iobase + 0x42);
122 outb((count >> 8) & 0xff, beep_iobase + 0x42);
123 } else {
124 /* disable counter 2 */
125 outb(inb_p(beep_iobase + 0x61) & 0xFC, beep_iobase + 0x61);
126 }
127
128 spin_unlock_irqrestore(&beep_lock, flags);
129
130 return 0;
131}
132
133static int __init init_isa_beep(struct sparc_isa_device *isa_dev)
134{
135 beep_iobase = isa_dev->resource.start;
136
Dmitry Torokhov76b7cdd2005-09-15 02:01:51 -0500137 sparcspkr_dev = input_allocate_device();
138 if (!sparcspkr_dev)
139 return -ENOMEM;
140
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 init_sparcspkr_struct();
142
Dmitry Torokhov76b7cdd2005-09-15 02:01:51 -0500143 sparcspkr_dev->name = "Sparc ISA Speaker";
144 sparcspkr_dev->event = isa_spkr_event;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145
146 input_register_device(&sparcspkr_dev);
147
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 return 0;
149}
150#endif
151
152static int __init sparcspkr_init(void)
153{
154 struct linux_ebus *ebus;
155 struct linux_ebus_device *edev = NULL;
156#ifdef CONFIG_SPARC64
157 struct sparc_isa_bridge *isa_br;
158 struct sparc_isa_device *isa_dev;
159#endif
160
161 for_each_ebus(ebus) {
162 for_each_ebusdev(edev, ebus) {
163 if (!strcmp(edev->prom_name, "beep"))
164 return init_ebus_beep(edev);
165 }
166 }
167#ifdef CONFIG_SPARC64
168 for_each_isa(isa_br) {
169 for_each_isadev(isa_dev, isa_br) {
170 /* A hack, the beep device's base lives in
171 * the DMA isa node.
172 */
173 if (!strcmp(isa_dev->prom_name, "dma"))
174 return init_isa_beep(isa_dev);
175 }
176 }
177#endif
178
179 return -ENODEV;
180}
181
182static void __exit sparcspkr_exit(void)
183{
Dmitry Torokhov76b7cdd2005-09-15 02:01:51 -0500184 input_unregister_device(sparcspkr_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185}
186
187module_init(sparcspkr_init);
188module_exit(sparcspkr_exit);