11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Driver for generic MPU-401 boards (UART mode only) 31da177e4SLinus Torvalds * Copyright (c) by Jaroslav Kysela <perex@suse.cz> 41da177e4SLinus Torvalds * Copyright (c) 2004 by Castet Matthieu <castet.matthieu@free.fr> 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 81da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 91da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 101da177e4SLinus Torvalds * (at your option) any later version. 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 131da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 141da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 151da177e4SLinus Torvalds * GNU General Public License for more details. 161da177e4SLinus Torvalds * 171da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 181da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 191da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 201da177e4SLinus Torvalds * 211da177e4SLinus Torvalds */ 221da177e4SLinus Torvalds 231da177e4SLinus Torvalds #include <sound/driver.h> 241da177e4SLinus Torvalds #include <linux/init.h> 251da177e4SLinus Torvalds #include <linux/pnp.h> 26b3fe9512STakashi Iwai #include <linux/err.h> 27b3fe9512STakashi Iwai #include <linux/platform_device.h> 281da177e4SLinus Torvalds #include <linux/moduleparam.h> 291da177e4SLinus Torvalds #include <sound/core.h> 301da177e4SLinus Torvalds #include <sound/mpu401.h> 311da177e4SLinus Torvalds #include <sound/initval.h> 321da177e4SLinus Torvalds 331da177e4SLinus Torvalds MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); 341da177e4SLinus Torvalds MODULE_DESCRIPTION("MPU-401 UART"); 351da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 361da177e4SLinus Torvalds 371da177e4SLinus Torvalds static int index[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -2}; /* exclude the first card */ 381da177e4SLinus Torvalds static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 391da177e4SLinus Torvalds static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ 401da177e4SLinus Torvalds #ifdef CONFIG_PNP 411da177e4SLinus Torvalds static int pnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; 421da177e4SLinus Torvalds #endif 431da177e4SLinus Torvalds static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* MPU-401 port number */ 441da177e4SLinus Torvalds static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* MPU-401 IRQ */ 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds module_param_array(index, int, NULL, 0444); 471da177e4SLinus Torvalds MODULE_PARM_DESC(index, "Index value for MPU-401 device."); 481da177e4SLinus Torvalds module_param_array(id, charp, NULL, 0444); 491da177e4SLinus Torvalds MODULE_PARM_DESC(id, "ID string for MPU-401 device."); 501da177e4SLinus Torvalds module_param_array(enable, bool, NULL, 0444); 511da177e4SLinus Torvalds MODULE_PARM_DESC(enable, "Enable MPU-401 device."); 521da177e4SLinus Torvalds #ifdef CONFIG_PNP 531da177e4SLinus Torvalds module_param_array(pnp, bool, NULL, 0444); 541da177e4SLinus Torvalds MODULE_PARM_DESC(pnp, "PnP detection for MPU-401 device."); 551da177e4SLinus Torvalds #endif 561da177e4SLinus Torvalds module_param_array(port, long, NULL, 0444); 571da177e4SLinus Torvalds MODULE_PARM_DESC(port, "Port # for MPU-401 device."); 581da177e4SLinus Torvalds module_param_array(irq, int, NULL, 0444); 591da177e4SLinus Torvalds MODULE_PARM_DESC(irq, "IRQ # for MPU-401 device."); 601da177e4SLinus Torvalds 61f7a9275dSClemens Ladisch static struct platform_device *platform_devices[SNDRV_CARDS]; 62f301ae6aSBjorn Helgaas static int pnp_registered; 63f301ae6aSBjorn Helgaas static unsigned int snd_mpu401_devices; 641da177e4SLinus Torvalds 65e1fad17bSTakashi Iwai static int snd_mpu401_create(int dev, struct snd_card **rcard) 661da177e4SLinus Torvalds { 67e1fad17bSTakashi Iwai struct snd_card *card; 681da177e4SLinus Torvalds int err; 691da177e4SLinus Torvalds 701da177e4SLinus Torvalds *rcard = NULL; 711da177e4SLinus Torvalds card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); 721da177e4SLinus Torvalds if (card == NULL) 731da177e4SLinus Torvalds return -ENOMEM; 741da177e4SLinus Torvalds strcpy(card->driver, "MPU-401 UART"); 751da177e4SLinus Torvalds strcpy(card->shortname, card->driver); 761da177e4SLinus Torvalds sprintf(card->longname, "%s at %#lx, ", card->shortname, port[dev]); 771da177e4SLinus Torvalds if (irq[dev] >= 0) { 781da177e4SLinus Torvalds sprintf(card->longname + strlen(card->longname), "irq %d", irq[dev]); 791da177e4SLinus Torvalds } else { 801da177e4SLinus Torvalds strcat(card->longname, "polled"); 811da177e4SLinus Torvalds } 821da177e4SLinus Torvalds 8316dab54bSTakashi Iwai if ((err = snd_mpu401_uart_new(card, 0, 841da177e4SLinus Torvalds MPU401_HW_MPU401, 851da177e4SLinus Torvalds port[dev], 0, 8616dab54bSTakashi Iwai irq[dev], irq[dev] >= 0 ? SA_INTERRUPT : 0, NULL)) < 0) { 871da177e4SLinus Torvalds printk(KERN_ERR "MPU401 not detected at 0x%lx\n", port[dev]); 8816dab54bSTakashi Iwai goto _err; 891da177e4SLinus Torvalds } 9016dab54bSTakashi Iwai 911da177e4SLinus Torvalds *rcard = card; 921da177e4SLinus Torvalds return 0; 9316dab54bSTakashi Iwai 9416dab54bSTakashi Iwai _err: 9516dab54bSTakashi Iwai snd_card_free(card); 9616dab54bSTakashi Iwai return err; 971da177e4SLinus Torvalds } 981da177e4SLinus Torvalds 99b3fe9512STakashi Iwai static int __devinit snd_mpu401_probe(struct platform_device *devptr) 1001da177e4SLinus Torvalds { 101b3fe9512STakashi Iwai int dev = devptr->id; 102b3fe9512STakashi Iwai int err; 103b3fe9512STakashi Iwai struct snd_card *card; 104b3fe9512STakashi Iwai 1051da177e4SLinus Torvalds if (port[dev] == SNDRV_AUTO_PORT) { 1061da177e4SLinus Torvalds snd_printk(KERN_ERR "specify port\n"); 1071da177e4SLinus Torvalds return -EINVAL; 1081da177e4SLinus Torvalds } 1091da177e4SLinus Torvalds if (irq[dev] == SNDRV_AUTO_IRQ) { 1101da177e4SLinus Torvalds snd_printk(KERN_ERR "specify or disable IRQ\n"); 1111da177e4SLinus Torvalds return -EINVAL; 1121da177e4SLinus Torvalds } 113b3fe9512STakashi Iwai err = snd_mpu401_create(dev, &card); 114b3fe9512STakashi Iwai if (err < 0) 115b3fe9512STakashi Iwai return err; 116b3fe9512STakashi Iwai snd_card_set_dev(card, &devptr->dev); 117b3fe9512STakashi Iwai if ((err = snd_card_register(card)) < 0) { 118b3fe9512STakashi Iwai snd_card_free(card); 119b3fe9512STakashi Iwai return err; 1201da177e4SLinus Torvalds } 121b3fe9512STakashi Iwai platform_set_drvdata(devptr, card); 122b3fe9512STakashi Iwai return 0; 123b3fe9512STakashi Iwai } 124b3fe9512STakashi Iwai 125b3fe9512STakashi Iwai static int __devexit snd_mpu401_remove(struct platform_device *devptr) 126b3fe9512STakashi Iwai { 127b3fe9512STakashi Iwai snd_card_free(platform_get_drvdata(devptr)); 128b3fe9512STakashi Iwai platform_set_drvdata(devptr, NULL); 129b3fe9512STakashi Iwai return 0; 130b3fe9512STakashi Iwai } 131b3fe9512STakashi Iwai 132b3fe9512STakashi Iwai #define SND_MPU401_DRIVER "snd_mpu401" 133b3fe9512STakashi Iwai 134b3fe9512STakashi Iwai static struct platform_driver snd_mpu401_driver = { 135b3fe9512STakashi Iwai .probe = snd_mpu401_probe, 136b3fe9512STakashi Iwai .remove = __devexit_p(snd_mpu401_remove), 137b3fe9512STakashi Iwai .driver = { 138b3fe9512STakashi Iwai .name = SND_MPU401_DRIVER 139b3fe9512STakashi Iwai }, 140b3fe9512STakashi Iwai }; 141b3fe9512STakashi Iwai 1421da177e4SLinus Torvalds 1431da177e4SLinus Torvalds #ifdef CONFIG_PNP 1441da177e4SLinus Torvalds 1451da177e4SLinus Torvalds #define IO_EXTENT 2 1461da177e4SLinus Torvalds 1471da177e4SLinus Torvalds static struct pnp_device_id snd_mpu401_pnpids[] = { 1481da177e4SLinus Torvalds { .id = "PNPb006" }, 1491da177e4SLinus Torvalds { .id = "" } 1501da177e4SLinus Torvalds }; 1511da177e4SLinus Torvalds 1521da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pnp, snd_mpu401_pnpids); 1531da177e4SLinus Torvalds 154fad43488SAndrew Morton static int __devinit snd_mpu401_pnp(int dev, struct pnp_dev *device, 1551da177e4SLinus Torvalds const struct pnp_device_id *id) 1561da177e4SLinus Torvalds { 1571da177e4SLinus Torvalds if (!pnp_port_valid(device, 0) || 1581da177e4SLinus Torvalds pnp_port_flags(device, 0) & IORESOURCE_DISABLED) { 1591da177e4SLinus Torvalds snd_printk(KERN_ERR "no PnP port\n"); 1601da177e4SLinus Torvalds return -ENODEV; 1611da177e4SLinus Torvalds } 1621da177e4SLinus Torvalds if (pnp_port_len(device, 0) < IO_EXTENT) { 1631da177e4SLinus Torvalds snd_printk(KERN_ERR "PnP port length is %ld, expected %d\n", 1641da177e4SLinus Torvalds pnp_port_len(device, 0), IO_EXTENT); 1651da177e4SLinus Torvalds return -ENODEV; 1661da177e4SLinus Torvalds } 1671da177e4SLinus Torvalds port[dev] = pnp_port_start(device, 0); 1681da177e4SLinus Torvalds 1691da177e4SLinus Torvalds if (!pnp_irq_valid(device, 0) || 1701da177e4SLinus Torvalds pnp_irq_flags(device, 0) & IORESOURCE_DISABLED) { 1711da177e4SLinus Torvalds snd_printk(KERN_WARNING "no PnP irq, using polling\n"); 1721da177e4SLinus Torvalds irq[dev] = -1; 1731da177e4SLinus Torvalds } else { 1741da177e4SLinus Torvalds irq[dev] = pnp_irq(device, 0); 1751da177e4SLinus Torvalds } 1761da177e4SLinus Torvalds return 0; 1771da177e4SLinus Torvalds } 1781da177e4SLinus Torvalds 1791da177e4SLinus Torvalds static int __devinit snd_mpu401_pnp_probe(struct pnp_dev *pnp_dev, 1801da177e4SLinus Torvalds const struct pnp_device_id *id) 1811da177e4SLinus Torvalds { 1821da177e4SLinus Torvalds static int dev; 183e1fad17bSTakashi Iwai struct snd_card *card; 1841da177e4SLinus Torvalds int err; 1851da177e4SLinus Torvalds 1861da177e4SLinus Torvalds for ( ; dev < SNDRV_CARDS; ++dev) { 1871da177e4SLinus Torvalds if (!enable[dev] || !pnp[dev]) 1881da177e4SLinus Torvalds continue; 1891da177e4SLinus Torvalds err = snd_mpu401_pnp(dev, pnp_dev, id); 1901da177e4SLinus Torvalds if (err < 0) 1911da177e4SLinus Torvalds return err; 1921da177e4SLinus Torvalds err = snd_mpu401_create(dev, &card); 1931da177e4SLinus Torvalds if (err < 0) 1941da177e4SLinus Torvalds return err; 195b3fe9512STakashi Iwai if ((err = snd_card_register(card)) < 0) { 196b3fe9512STakashi Iwai snd_card_free(card); 197b3fe9512STakashi Iwai return err; 198b3fe9512STakashi Iwai } 1991da177e4SLinus Torvalds snd_card_set_dev(card, &pnp_dev->dev); 2001da177e4SLinus Torvalds pnp_set_drvdata(pnp_dev, card); 201f301ae6aSBjorn Helgaas snd_mpu401_devices++; 2021da177e4SLinus Torvalds ++dev; 2031da177e4SLinus Torvalds return 0; 2041da177e4SLinus Torvalds } 2051da177e4SLinus Torvalds return -ENODEV; 2061da177e4SLinus Torvalds } 2071da177e4SLinus Torvalds 2081da177e4SLinus Torvalds static void __devexit snd_mpu401_pnp_remove(struct pnp_dev *dev) 2091da177e4SLinus Torvalds { 210e1fad17bSTakashi Iwai struct snd_card *card = (struct snd_card *) pnp_get_drvdata(dev); 2111da177e4SLinus Torvalds 2121da177e4SLinus Torvalds snd_card_disconnect(card); 2131da177e4SLinus Torvalds snd_card_free_in_thread(card); 2141da177e4SLinus Torvalds } 2151da177e4SLinus Torvalds 2161da177e4SLinus Torvalds static struct pnp_driver snd_mpu401_pnp_driver = { 2171da177e4SLinus Torvalds .name = "mpu401", 2181da177e4SLinus Torvalds .id_table = snd_mpu401_pnpids, 2191da177e4SLinus Torvalds .probe = snd_mpu401_pnp_probe, 2201da177e4SLinus Torvalds .remove = __devexit_p(snd_mpu401_pnp_remove), 2211da177e4SLinus Torvalds }; 2221da177e4SLinus Torvalds #else 2231da177e4SLinus Torvalds static struct pnp_driver snd_mpu401_pnp_driver; 2241da177e4SLinus Torvalds #endif 2251da177e4SLinus Torvalds 226f7a9275dSClemens Ladisch static void __init_or_module snd_mpu401_unregister_all(void) 227f7a9275dSClemens Ladisch { 228f7a9275dSClemens Ladisch int i; 229f7a9275dSClemens Ladisch 230f7a9275dSClemens Ladisch if (pnp_registered) 231f7a9275dSClemens Ladisch pnp_unregister_driver(&snd_mpu401_pnp_driver); 232f7a9275dSClemens Ladisch for (i = 0; i < ARRAY_SIZE(platform_devices); ++i) 233f7a9275dSClemens Ladisch platform_device_unregister(platform_devices[i]); 234f7a9275dSClemens Ladisch platform_driver_unregister(&snd_mpu401_driver); 235f7a9275dSClemens Ladisch } 236f7a9275dSClemens Ladisch 2371da177e4SLinus Torvalds static int __init alsa_card_mpu401_init(void) 2381da177e4SLinus Torvalds { 239f301ae6aSBjorn Helgaas int i, err; 2401da177e4SLinus Torvalds 241b3fe9512STakashi Iwai if ((err = platform_driver_register(&snd_mpu401_driver)) < 0) 242b3fe9512STakashi Iwai return err; 243b3fe9512STakashi Iwai 2448278ca8fSTakashi Iwai for (i = 0; i < SNDRV_CARDS; i++) { 245b3fe9512STakashi Iwai struct platform_device *device; 2468278ca8fSTakashi Iwai if (! enable[i]) 2478278ca8fSTakashi Iwai continue; 2481da177e4SLinus Torvalds #ifdef CONFIG_PNP 249b3fe9512STakashi Iwai if (pnp[i]) 2501da177e4SLinus Torvalds continue; 2511da177e4SLinus Torvalds #endif 252b3fe9512STakashi Iwai device = platform_device_register_simple(SND_MPU401_DRIVER, 253b3fe9512STakashi Iwai i, NULL, 0); 254a182ee98SRene Herman if (IS_ERR(device)) 255a182ee98SRene Herman continue; 256f7a9275dSClemens Ladisch platform_devices[i] = device; 257f301ae6aSBjorn Helgaas snd_mpu401_devices++; 2581da177e4SLinus Torvalds } 259f301ae6aSBjorn Helgaas err = pnp_register_driver(&snd_mpu401_pnp_driver); 260f301ae6aSBjorn Helgaas if (!err) 2611da177e4SLinus Torvalds pnp_registered = 1; 2621da177e4SLinus Torvalds 263f301ae6aSBjorn Helgaas if (!snd_mpu401_devices) { 2641da177e4SLinus Torvalds #ifdef MODULE 2651da177e4SLinus Torvalds printk(KERN_ERR "MPU-401 device not found or device busy\n"); 2661da177e4SLinus Torvalds #endif 267a182ee98SRene Herman snd_mpu401_unregister_all(); 268a182ee98SRene Herman return -ENODEV; 2691da177e4SLinus Torvalds } 2701da177e4SLinus Torvalds return 0; 2711da177e4SLinus Torvalds } 2721da177e4SLinus Torvalds 2731da177e4SLinus Torvalds static void __exit alsa_card_mpu401_exit(void) 2741da177e4SLinus Torvalds { 275f7a9275dSClemens Ladisch snd_mpu401_unregister_all(); 2761da177e4SLinus Torvalds } 2771da177e4SLinus Torvalds 2781da177e4SLinus Torvalds module_init(alsa_card_mpu401_init) 2791da177e4SLinus Torvalds module_exit(alsa_card_mpu401_exit) 280