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