0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/init.h>
0010 #include <linux/module.h>
0011 #include <linux/platform_device.h>
0012 #include <sound/core.h>
0013 #include <sound/initval.h>
0014 #include <sound/pcm.h>
0015 #include <linux/input.h>
0016 #include <linux/delay.h>
0017 #include <linux/bitops.h>
0018 #include <linux/mm.h>
0019 #include "pcsp_input.h"
0020 #include "pcsp.h"
0021
0022 MODULE_AUTHOR("Stas Sergeev <stsp@users.sourceforge.net>");
0023 MODULE_DESCRIPTION("PC-Speaker driver");
0024 MODULE_LICENSE("GPL");
0025 MODULE_ALIAS("platform:pcspkr");
0026
0027 static int index = SNDRV_DEFAULT_IDX1;
0028 static char *id = SNDRV_DEFAULT_STR1;
0029 static bool enable = SNDRV_DEFAULT_ENABLE1;
0030 static bool nopcm;
0031
0032 module_param(index, int, 0444);
0033 MODULE_PARM_DESC(index, "Index value for pcsp soundcard.");
0034 module_param(id, charp, 0444);
0035 MODULE_PARM_DESC(id, "ID string for pcsp soundcard.");
0036 module_param(enable, bool, 0444);
0037 MODULE_PARM_DESC(enable, "Enable PC-Speaker sound.");
0038 module_param(nopcm, bool, 0444);
0039 MODULE_PARM_DESC(nopcm, "Disable PC-Speaker PCM sound. Only beeps remain.");
0040
0041 struct snd_pcsp pcsp_chip;
0042
0043 static int snd_pcsp_create(struct snd_card *card)
0044 {
0045 unsigned int resolution = hrtimer_resolution;
0046 int div, min_div, order;
0047
0048 if (!nopcm) {
0049 if (resolution > PCSP_MAX_PERIOD_NS) {
0050 printk(KERN_ERR "PCSP: Timer resolution is not sufficient "
0051 "(%unS)\n", resolution);
0052 printk(KERN_ERR "PCSP: Make sure you have HPET and ACPI "
0053 "enabled.\n");
0054 printk(KERN_ERR "PCSP: Turned into nopcm mode.\n");
0055 nopcm = 1;
0056 }
0057 }
0058
0059 if (loops_per_jiffy >= PCSP_MIN_LPJ && resolution <= PCSP_MIN_PERIOD_NS)
0060 min_div = MIN_DIV;
0061 else
0062 min_div = MAX_DIV;
0063 #if PCSP_DEBUG
0064 printk(KERN_DEBUG "PCSP: lpj=%li, min_div=%i, res=%u\n",
0065 loops_per_jiffy, min_div, resolution);
0066 #endif
0067
0068 div = MAX_DIV / min_div;
0069 order = fls(div) - 1;
0070
0071 pcsp_chip.max_treble = min(order, PCSP_MAX_TREBLE);
0072 pcsp_chip.treble = min(pcsp_chip.max_treble, PCSP_DEFAULT_TREBLE);
0073 pcsp_chip.playback_ptr = 0;
0074 pcsp_chip.period_ptr = 0;
0075 atomic_set(&pcsp_chip.timer_active, 0);
0076 pcsp_chip.enable = 1;
0077 pcsp_chip.pcspkr = 1;
0078
0079 spin_lock_init(&pcsp_chip.substream_lock);
0080
0081 pcsp_chip.card = card;
0082 pcsp_chip.port = 0x61;
0083 pcsp_chip.irq = -1;
0084 pcsp_chip.dma = -1;
0085 card->private_data = &pcsp_chip;
0086
0087 return 0;
0088 }
0089
0090 static void pcsp_stop_beep(struct snd_pcsp *chip);
0091
0092 static void alsa_card_pcsp_free(struct snd_card *card)
0093 {
0094 pcsp_stop_beep(card->private_data);
0095 }
0096
0097 static int snd_card_pcsp_probe(int devnum, struct device *dev)
0098 {
0099 struct snd_card *card;
0100 int err;
0101
0102 if (devnum != 0)
0103 return -EINVAL;
0104
0105 hrtimer_init(&pcsp_chip.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
0106 pcsp_chip.timer.function = pcsp_do_timer;
0107
0108 err = snd_devm_card_new(dev, index, id, THIS_MODULE, 0, &card);
0109 if (err < 0)
0110 return err;
0111
0112 err = snd_pcsp_create(card);
0113 if (err < 0)
0114 return err;
0115
0116 if (!nopcm) {
0117 err = snd_pcsp_new_pcm(&pcsp_chip);
0118 if (err < 0)
0119 return err;
0120 }
0121 err = snd_pcsp_new_mixer(&pcsp_chip, nopcm);
0122 if (err < 0)
0123 return err;
0124
0125 strcpy(card->driver, "PC-Speaker");
0126 strcpy(card->shortname, "pcsp");
0127 sprintf(card->longname, "Internal PC-Speaker at port 0x%x",
0128 pcsp_chip.port);
0129
0130 err = snd_card_register(card);
0131 if (err < 0)
0132 return err;
0133 card->private_free = alsa_card_pcsp_free;
0134
0135 return 0;
0136 }
0137
0138 static int alsa_card_pcsp_init(struct device *dev)
0139 {
0140 int err;
0141
0142 err = snd_card_pcsp_probe(0, dev);
0143 if (err) {
0144 printk(KERN_ERR "PC-Speaker initialization failed.\n");
0145 return err;
0146 }
0147
0148
0149 if (debug_pagealloc_enabled()) {
0150 printk(KERN_WARNING "PCSP: CONFIG_DEBUG_PAGEALLOC is enabled, "
0151 "which may make the sound noisy.\n");
0152 }
0153
0154 return 0;
0155 }
0156
0157 static int pcsp_probe(struct platform_device *dev)
0158 {
0159 int err;
0160
0161 err = pcspkr_input_init(&pcsp_chip.input_dev, &dev->dev);
0162 if (err < 0)
0163 return err;
0164
0165 err = alsa_card_pcsp_init(&dev->dev);
0166 if (err < 0)
0167 return err;
0168
0169 platform_set_drvdata(dev, &pcsp_chip);
0170 return 0;
0171 }
0172
0173 static void pcsp_stop_beep(struct snd_pcsp *chip)
0174 {
0175 pcsp_sync_stop(chip);
0176 pcspkr_stop_sound();
0177 }
0178
0179 #ifdef CONFIG_PM_SLEEP
0180 static int pcsp_suspend(struct device *dev)
0181 {
0182 struct snd_pcsp *chip = dev_get_drvdata(dev);
0183 pcsp_stop_beep(chip);
0184 return 0;
0185 }
0186
0187 static SIMPLE_DEV_PM_OPS(pcsp_pm, pcsp_suspend, NULL);
0188 #define PCSP_PM_OPS &pcsp_pm
0189 #else
0190 #define PCSP_PM_OPS NULL
0191 #endif
0192
0193 static void pcsp_shutdown(struct platform_device *dev)
0194 {
0195 struct snd_pcsp *chip = platform_get_drvdata(dev);
0196 pcsp_stop_beep(chip);
0197 }
0198
0199 static struct platform_driver pcsp_platform_driver = {
0200 .driver = {
0201 .name = "pcspkr",
0202 .pm = PCSP_PM_OPS,
0203 },
0204 .probe = pcsp_probe,
0205 .shutdown = pcsp_shutdown,
0206 };
0207
0208 static int __init pcsp_init(void)
0209 {
0210 if (!enable)
0211 return -ENODEV;
0212 return platform_driver_register(&pcsp_platform_driver);
0213 }
0214
0215 static void __exit pcsp_exit(void)
0216 {
0217 platform_driver_unregister(&pcsp_platform_driver);
0218 }
0219
0220 module_init(pcsp_init);
0221 module_exit(pcsp_exit);