]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Driver for PC-speaker like devices found on various Sparc systems. | |
3 | * | |
4 | * Copyright (c) 2002 Vojtech Pavlik | |
a2bd4fd1 | 5 | * Copyright (c) 2002, 2006 David S. Miller (davem@davemloft.net) |
1da177e4 | 6 | */ |
1da177e4 LT |
7 | #include <linux/kernel.h> |
8 | #include <linux/module.h> | |
9 | #include <linux/init.h> | |
10 | #include <linux/input.h> | |
f5b64078 | 11 | #include <linux/platform_device.h> |
1da177e4 LT |
12 | |
13 | #include <asm/io.h> | |
14 | #include <asm/ebus.h> | |
1da177e4 | 15 | #include <asm/isa.h> |
1da177e4 | 16 | |
a2bd4fd1 | 17 | MODULE_AUTHOR("David S. Miller <davem@davemloft.net>"); |
76b7cddf | 18 | MODULE_DESCRIPTION("Sparc Speaker beeper driver"); |
1da177e4 LT |
19 | MODULE_LICENSE("GPL"); |
20 | ||
a2bd4fd1 DM |
21 | struct sparcspkr_state { |
22 | const char *name; | |
23 | unsigned long iobase; | |
24 | int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); | |
25 | spinlock_t lock; | |
26 | struct input_dev *input_dev; | |
27 | }; | |
1da177e4 LT |
28 | |
29 | static int ebus_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) | |
30 | { | |
293e6392 | 31 | struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent); |
1da177e4 LT |
32 | unsigned int count = 0; |
33 | unsigned long flags; | |
34 | ||
35 | if (type != EV_SND) | |
36 | return -1; | |
37 | ||
38 | switch (code) { | |
39 | case SND_BELL: if (value) value = 1000; | |
40 | case SND_TONE: break; | |
41 | default: return -1; | |
42 | } | |
43 | ||
44 | if (value > 20 && value < 32767) | |
45 | count = 1193182 / value; | |
46 | ||
a2bd4fd1 | 47 | spin_lock_irqsave(&state->lock, flags); |
1da177e4 LT |
48 | |
49 | /* EBUS speaker only has on/off state, the frequency does not | |
50 | * appear to be programmable. | |
51 | */ | |
a2bd4fd1 DM |
52 | if (state->iobase & 0x2UL) |
53 | outb(!!count, state->iobase); | |
f5b64078 | 54 | else |
a2bd4fd1 | 55 | outl(!!count, state->iobase); |
1da177e4 | 56 | |
a2bd4fd1 | 57 | spin_unlock_irqrestore(&state->lock, flags); |
1da177e4 LT |
58 | |
59 | return 0; | |
60 | } | |
61 | ||
1da177e4 LT |
62 | static int isa_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) |
63 | { | |
293e6392 | 64 | struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent); |
1da177e4 LT |
65 | unsigned int count = 0; |
66 | unsigned long flags; | |
67 | ||
68 | if (type != EV_SND) | |
69 | return -1; | |
70 | ||
71 | switch (code) { | |
72 | case SND_BELL: if (value) value = 1000; | |
73 | case SND_TONE: break; | |
74 | default: return -1; | |
75 | } | |
76 | ||
77 | if (value > 20 && value < 32767) | |
78 | count = 1193182 / value; | |
79 | ||
a2bd4fd1 | 80 | spin_lock_irqsave(&state->lock, flags); |
1da177e4 LT |
81 | |
82 | if (count) { | |
83 | /* enable counter 2 */ | |
a2bd4fd1 | 84 | outb(inb(state->iobase + 0x61) | 3, state->iobase + 0x61); |
1da177e4 | 85 | /* set command for counter 2, 2 byte write */ |
a2bd4fd1 | 86 | outb(0xB6, state->iobase + 0x43); |
1da177e4 | 87 | /* select desired HZ */ |
a2bd4fd1 DM |
88 | outb(count & 0xff, state->iobase + 0x42); |
89 | outb((count >> 8) & 0xff, state->iobase + 0x42); | |
1da177e4 LT |
90 | } else { |
91 | /* disable counter 2 */ | |
a2bd4fd1 | 92 | outb(inb_p(state->iobase + 0x61) & 0xFC, state->iobase + 0x61); |
1da177e4 LT |
93 | } |
94 | ||
a2bd4fd1 | 95 | spin_unlock_irqrestore(&state->lock, flags); |
1da177e4 LT |
96 | |
97 | return 0; | |
98 | } | |
99 | ||
a2bd4fd1 | 100 | static int __devinit sparcspkr_probe(struct device *dev) |
1da177e4 | 101 | { |
a2bd4fd1 | 102 | struct sparcspkr_state *state = dev_get_drvdata(dev); |
f5b64078 DT |
103 | struct input_dev *input_dev; |
104 | int error; | |
1da177e4 | 105 | |
f5b64078 DT |
106 | input_dev = input_allocate_device(); |
107 | if (!input_dev) | |
76b7cddf DT |
108 | return -ENOMEM; |
109 | ||
a2bd4fd1 | 110 | input_dev->name = state->name; |
f5b64078 DT |
111 | input_dev->phys = "sparc/input0"; |
112 | input_dev->id.bustype = BUS_ISA; | |
113 | input_dev->id.vendor = 0x001f; | |
114 | input_dev->id.product = 0x0001; | |
115 | input_dev->id.version = 0x0100; | |
293e6392 | 116 | input_dev->dev.parent = dev; |
1da177e4 | 117 | |
f5b64078 DT |
118 | input_dev->evbit[0] = BIT(EV_SND); |
119 | input_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE); | |
1da177e4 | 120 | |
a2bd4fd1 | 121 | input_dev->event = state->event; |
f5b64078 DT |
122 | |
123 | error = input_register_device(input_dev); | |
124 | if (error) { | |
125 | input_free_device(input_dev); | |
126 | return error; | |
127 | } | |
128 | ||
a2bd4fd1 | 129 | state->input_dev = input_dev; |
1da177e4 | 130 | |
1da177e4 LT |
131 | return 0; |
132 | } | |
f5b64078 | 133 | |
a2bd4fd1 | 134 | static int __devexit sparcspkr_remove(struct of_device *dev) |
f5b64078 | 135 | { |
a2bd4fd1 DM |
136 | struct sparcspkr_state *state = dev_get_drvdata(&dev->dev); |
137 | struct input_dev *input_dev = state->input_dev; | |
f5b64078 | 138 | |
f5b64078 | 139 | /* turn off the speaker */ |
a2bd4fd1 DM |
140 | state->event(input_dev, EV_SND, SND_BELL, 0); |
141 | ||
142 | input_unregister_device(input_dev); | |
143 | ||
144 | dev_set_drvdata(&dev->dev, NULL); | |
145 | kfree(state); | |
f5b64078 DT |
146 | |
147 | return 0; | |
148 | } | |
149 | ||
a2bd4fd1 | 150 | static int sparcspkr_shutdown(struct of_device *dev) |
f5b64078 | 151 | { |
a2bd4fd1 DM |
152 | struct sparcspkr_state *state = dev_get_drvdata(&dev->dev); |
153 | struct input_dev *input_dev = state->input_dev; | |
154 | ||
f5b64078 | 155 | /* turn off the speaker */ |
a2bd4fd1 DM |
156 | state->event(input_dev, EV_SND, SND_BELL, 0); |
157 | ||
158 | return 0; | |
159 | } | |
160 | ||
161 | static int __devinit ebus_beep_probe(struct of_device *dev, const struct of_device_id *match) | |
162 | { | |
163 | struct linux_ebus_device *edev = to_ebus_device(&dev->dev); | |
164 | struct sparcspkr_state *state; | |
165 | int err; | |
166 | ||
167 | state = kzalloc(sizeof(*state), GFP_KERNEL); | |
168 | if (!state) | |
169 | return -ENOMEM; | |
170 | ||
171 | state->name = "Sparc EBUS Speaker"; | |
172 | state->iobase = edev->resource[0].start; | |
173 | state->event = ebus_spkr_event; | |
174 | spin_lock_init(&state->lock); | |
175 | ||
176 | dev_set_drvdata(&dev->dev, state); | |
177 | ||
178 | err = sparcspkr_probe(&dev->dev); | |
179 | if (err) { | |
180 | dev_set_drvdata(&dev->dev, NULL); | |
181 | kfree(state); | |
182 | } | |
183 | ||
184 | return 0; | |
f5b64078 DT |
185 | } |
186 | ||
a2bd4fd1 DM |
187 | static struct of_device_id ebus_beep_match[] = { |
188 | { | |
189 | .name = "beep", | |
f5b64078 | 190 | }, |
a2bd4fd1 | 191 | {}, |
f5b64078 DT |
192 | }; |
193 | ||
a2bd4fd1 DM |
194 | static struct of_platform_driver ebus_beep_driver = { |
195 | .name = "beep", | |
196 | .match_table = ebus_beep_match, | |
197 | .probe = ebus_beep_probe, | |
198 | .remove = sparcspkr_remove, | |
199 | .shutdown = sparcspkr_shutdown, | |
200 | }; | |
f5b64078 | 201 | |
a2bd4fd1 | 202 | static int __devinit isa_beep_probe(struct of_device *dev, const struct of_device_id *match) |
f5b64078 | 203 | { |
a2bd4fd1 DM |
204 | struct sparc_isa_device *idev = to_isa_device(&dev->dev); |
205 | struct sparcspkr_state *state; | |
206 | int err; | |
f5b64078 | 207 | |
a2bd4fd1 DM |
208 | state = kzalloc(sizeof(*state), GFP_KERNEL); |
209 | if (!state) | |
210 | return -ENOMEM; | |
f5b64078 | 211 | |
a2bd4fd1 DM |
212 | state->name = "Sparc ISA Speaker"; |
213 | state->iobase = idev->resource.start; | |
214 | state->event = isa_spkr_event; | |
215 | spin_lock_init(&state->lock); | |
216 | ||
217 | dev_set_drvdata(&dev->dev, state); | |
f5b64078 | 218 | |
a2bd4fd1 DM |
219 | err = sparcspkr_probe(&dev->dev); |
220 | if (err) { | |
221 | dev_set_drvdata(&dev->dev, NULL); | |
222 | kfree(state); | |
223 | } | |
f5b64078 DT |
224 | |
225 | return 0; | |
a2bd4fd1 | 226 | } |
f5b64078 | 227 | |
a2bd4fd1 DM |
228 | static struct of_device_id isa_beep_match[] = { |
229 | { | |
230 | .name = "dma", | |
231 | }, | |
232 | {}, | |
233 | }; | |
f5b64078 | 234 | |
a2bd4fd1 DM |
235 | static struct of_platform_driver isa_beep_driver = { |
236 | .name = "beep", | |
237 | .match_table = isa_beep_match, | |
238 | .probe = isa_beep_probe, | |
239 | .remove = sparcspkr_remove, | |
240 | .shutdown = sparcspkr_shutdown, | |
241 | }; | |
1da177e4 LT |
242 | |
243 | static int __init sparcspkr_init(void) | |
244 | { | |
a2bd4fd1 DM |
245 | int err = of_register_driver(&ebus_beep_driver, &ebus_bus_type); |
246 | ||
247 | if (!err) { | |
248 | err = of_register_driver(&isa_beep_driver, &isa_bus_type); | |
249 | if (err) | |
250 | of_unregister_driver(&ebus_beep_driver); | |
1da177e4 | 251 | } |
1da177e4 | 252 | |
a2bd4fd1 | 253 | return err; |
1da177e4 LT |
254 | } |
255 | ||
256 | static void __exit sparcspkr_exit(void) | |
257 | { | |
a2bd4fd1 DM |
258 | of_unregister_driver(&ebus_beep_driver); |
259 | of_unregister_driver(&isa_beep_driver); | |
1da177e4 LT |
260 | } |
261 | ||
262 | module_init(sparcspkr_init); | |
263 | module_exit(sparcspkr_exit); |