• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/sound/drivers/pcsp/
1/*
2 * PC-Speaker driver for Linux
3 *
4 * Copyright (C) 1997-2001  David Woodhouse
5 * Copyright (C) 2001-2008  Stas Sergeev
6 */
7
8#include <linux/init.h>
9#include <linux/moduleparam.h>
10#include <linux/platform_device.h>
11#include <sound/core.h>
12#include <sound/initval.h>
13#include <sound/pcm.h>
14#include <linux/input.h>
15#include <linux/delay.h>
16#include <asm/bitops.h>
17#include "pcsp_input.h"
18#include "pcsp.h"
19
20MODULE_AUTHOR("Stas Sergeev <stsp@users.sourceforge.net>");
21MODULE_DESCRIPTION("PC-Speaker driver");
22MODULE_LICENSE("GPL");
23MODULE_SUPPORTED_DEVICE("{{PC-Speaker, pcsp}}");
24MODULE_ALIAS("platform:pcspkr");
25
26static int index = SNDRV_DEFAULT_IDX1;	/* Index 0-MAX */
27static char *id = SNDRV_DEFAULT_STR1;	/* ID for this card */
28static int enable = SNDRV_DEFAULT_ENABLE1;	/* Enable this card */
29static int nopcm;	/* Disable PCM capability of the driver */
30
31module_param(index, int, 0444);
32MODULE_PARM_DESC(index, "Index value for pcsp soundcard.");
33module_param(id, charp, 0444);
34MODULE_PARM_DESC(id, "ID string for pcsp soundcard.");
35module_param(enable, bool, 0444);
36MODULE_PARM_DESC(enable, "Enable PC-Speaker sound.");
37module_param(nopcm, bool, 0444);
38MODULE_PARM_DESC(nopcm, "Disable PC-Speaker PCM sound. Only beeps remain.");
39
40struct snd_pcsp pcsp_chip;
41
42static int __devinit snd_pcsp_create(struct snd_card *card)
43{
44	static struct snd_device_ops ops = { };
45	struct timespec tp;
46	int err;
47	int div, min_div, order;
48
49	if (!nopcm) {
50		hrtimer_get_res(CLOCK_MONOTONIC, &tp);
51		if (tp.tv_sec || tp.tv_nsec > PCSP_MAX_PERIOD_NS) {
52			printk(KERN_ERR "PCSP: Timer resolution is not sufficient "
53				"(%linS)\n", tp.tv_nsec);
54			printk(KERN_ERR "PCSP: Make sure you have HPET and ACPI "
55				"enabled.\n");
56			printk(KERN_ERR "PCSP: Turned into nopcm mode.\n");
57			nopcm = 1;
58		}
59	}
60
61	if (loops_per_jiffy >= PCSP_MIN_LPJ && tp.tv_nsec <= PCSP_MIN_PERIOD_NS)
62		min_div = MIN_DIV;
63	else
64		min_div = MAX_DIV;
65#if PCSP_DEBUG
66	printk(KERN_DEBUG "PCSP: lpj=%li, min_div=%i, res=%li\n",
67	       loops_per_jiffy, min_div, tp.tv_nsec);
68#endif
69
70	div = MAX_DIV / min_div;
71	order = fls(div) - 1;
72
73	pcsp_chip.max_treble = min(order, PCSP_MAX_TREBLE);
74	pcsp_chip.treble = min(pcsp_chip.max_treble, PCSP_DEFAULT_TREBLE);
75	pcsp_chip.playback_ptr = 0;
76	pcsp_chip.period_ptr = 0;
77	atomic_set(&pcsp_chip.timer_active, 0);
78	pcsp_chip.enable = 1;
79	pcsp_chip.pcspkr = 1;
80
81	spin_lock_init(&pcsp_chip.substream_lock);
82
83	pcsp_chip.card = card;
84	pcsp_chip.port = 0x61;
85	pcsp_chip.irq = -1;
86	pcsp_chip.dma = -1;
87
88	/* Register device */
89	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, &pcsp_chip, &ops);
90	if (err < 0)
91		return err;
92
93	return 0;
94}
95
96static int __devinit snd_card_pcsp_probe(int devnum, struct device *dev)
97{
98	struct snd_card *card;
99	int err;
100
101	if (devnum != 0)
102		return -EINVAL;
103
104	hrtimer_init(&pcsp_chip.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
105	pcsp_chip.timer.function = pcsp_do_timer;
106
107	err = snd_card_create(index, id, THIS_MODULE, 0, &card);
108	if (err < 0)
109		return err;
110
111	err = snd_pcsp_create(card);
112	if (err < 0) {
113		snd_card_free(card);
114		return err;
115	}
116	if (!nopcm) {
117		err = snd_pcsp_new_pcm(&pcsp_chip);
118		if (err < 0) {
119			snd_card_free(card);
120			return err;
121		}
122	}
123	err = snd_pcsp_new_mixer(&pcsp_chip, nopcm);
124	if (err < 0) {
125		snd_card_free(card);
126		return err;
127	}
128
129	snd_card_set_dev(pcsp_chip.card, dev);
130
131	strcpy(card->driver, "PC-Speaker");
132	strcpy(card->shortname, "pcsp");
133	sprintf(card->longname, "Internal PC-Speaker at port 0x%x",
134		pcsp_chip.port);
135
136	err = snd_card_register(card);
137	if (err < 0) {
138		snd_card_free(card);
139		return err;
140	}
141
142	return 0;
143}
144
145static int __devinit alsa_card_pcsp_init(struct device *dev)
146{
147	int err;
148
149	err = snd_card_pcsp_probe(0, dev);
150	if (err) {
151		printk(KERN_ERR "PC-Speaker initialization failed.\n");
152		return err;
153	}
154
155#ifdef CONFIG_DEBUG_PAGEALLOC
156	/* Well, CONFIG_DEBUG_PAGEALLOC makes the sound horrible. Lets alert */
157	printk(KERN_WARNING "PCSP: CONFIG_DEBUG_PAGEALLOC is enabled, "
158	       "which may make the sound noisy.\n");
159#endif
160
161	return 0;
162}
163
164static void __devexit alsa_card_pcsp_exit(struct snd_pcsp *chip)
165{
166	snd_card_free(chip->card);
167}
168
169static int __devinit pcsp_probe(struct platform_device *dev)
170{
171	int err;
172
173	err = pcspkr_input_init(&pcsp_chip.input_dev, &dev->dev);
174	if (err < 0)
175		return err;
176
177	err = alsa_card_pcsp_init(&dev->dev);
178	if (err < 0) {
179		pcspkr_input_remove(pcsp_chip.input_dev);
180		return err;
181	}
182
183	platform_set_drvdata(dev, &pcsp_chip);
184	return 0;
185}
186
187static int __devexit pcsp_remove(struct platform_device *dev)
188{
189	struct snd_pcsp *chip = platform_get_drvdata(dev);
190	alsa_card_pcsp_exit(chip);
191	pcspkr_input_remove(chip->input_dev);
192	platform_set_drvdata(dev, NULL);
193	return 0;
194}
195
196static void pcsp_stop_beep(struct snd_pcsp *chip)
197{
198	pcsp_sync_stop(chip);
199	pcspkr_stop_sound();
200}
201
202#ifdef CONFIG_PM
203static int pcsp_suspend(struct platform_device *dev, pm_message_t state)
204{
205	struct snd_pcsp *chip = platform_get_drvdata(dev);
206	pcsp_stop_beep(chip);
207	snd_pcm_suspend_all(chip->pcm);
208	return 0;
209}
210#else
211#define pcsp_suspend NULL
212#endif	/* CONFIG_PM */
213
214static void pcsp_shutdown(struct platform_device *dev)
215{
216	struct snd_pcsp *chip = platform_get_drvdata(dev);
217	pcsp_stop_beep(chip);
218}
219
220static struct platform_driver pcsp_platform_driver = {
221	.driver		= {
222		.name	= "pcspkr",
223		.owner	= THIS_MODULE,
224	},
225	.probe		= pcsp_probe,
226	.remove		= __devexit_p(pcsp_remove),
227	.suspend	= pcsp_suspend,
228	.shutdown	= pcsp_shutdown,
229};
230
231static int __init pcsp_init(void)
232{
233	if (!enable)
234		return -ENODEV;
235	return platform_driver_register(&pcsp_platform_driver);
236}
237
238static void __exit pcsp_exit(void)
239{
240	platform_driver_unregister(&pcsp_platform_driver);
241}
242
243module_init(pcsp_init);
244module_exit(pcsp_exit);
245