11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Driver for generic MPU-401 boards (UART mode only) 3c1017a4cSJaroslav Kysela * Copyright (c) by Jaroslav Kysela <perex@perex.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 <linux/init.h> 241da177e4SLinus Torvalds #include <linux/pnp.h> 25b3fe9512STakashi Iwai #include <linux/err.h> 26b3fe9512STakashi Iwai #include <linux/platform_device.h> 2765a77217SPaul Gortmaker #include <linux/module.h> 281da177e4SLinus Torvalds #include <sound/core.h> 291da177e4SLinus Torvalds #include <sound/mpu401.h> 301da177e4SLinus Torvalds #include <sound/initval.h> 311da177e4SLinus Torvalds 32c1017a4cSJaroslav Kysela MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 331da177e4SLinus Torvalds MODULE_DESCRIPTION("MPU-401 UART"); 341da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 351da177e4SLinus Torvalds 361da177e4SLinus Torvalds static int index[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -2}; /* exclude the first card */ 371da177e4SLinus Torvalds static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 381da177e4SLinus Torvalds static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ 391da177e4SLinus Torvalds #ifdef CONFIG_PNP 401da177e4SLinus Torvalds static int pnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; 411da177e4SLinus Torvalds #endif 421da177e4SLinus Torvalds static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* MPU-401 port number */ 431da177e4SLinus Torvalds static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* MPU-401 IRQ */ 448f7ba051STakashi Iwai static int uart_enter[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; 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."); 608f7ba051STakashi Iwai module_param_array(uart_enter, bool, NULL, 0444); 618f7ba051STakashi Iwai MODULE_PARM_DESC(uart_enter, "Issue UART_ENTER command at open."); 621da177e4SLinus Torvalds 63f7a9275dSClemens Ladisch static struct platform_device *platform_devices[SNDRV_CARDS]; 64f301ae6aSBjorn Helgaas static int pnp_registered; 65f301ae6aSBjorn Helgaas static unsigned int snd_mpu401_devices; 661da177e4SLinus Torvalds 67e1fad17bSTakashi Iwai static int snd_mpu401_create(int dev, struct snd_card **rcard) 681da177e4SLinus Torvalds { 69e1fad17bSTakashi Iwai struct snd_card *card; 701da177e4SLinus Torvalds int err; 711da177e4SLinus Torvalds 72c1099fcbSClemens Ladisch if (!uart_enter[dev]) 73c1099fcbSClemens Ladisch snd_printk(KERN_ERR "the uart_enter option is obsolete; remove it\n"); 74c1099fcbSClemens Ladisch 751da177e4SLinus Torvalds *rcard = NULL; 76bd7dd77cSTakashi Iwai err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); 77bd7dd77cSTakashi Iwai if (err < 0) 78bd7dd77cSTakashi Iwai return err; 791da177e4SLinus Torvalds strcpy(card->driver, "MPU-401 UART"); 801da177e4SLinus Torvalds strcpy(card->shortname, card->driver); 811da177e4SLinus Torvalds sprintf(card->longname, "%s at %#lx, ", card->shortname, port[dev]); 821da177e4SLinus Torvalds if (irq[dev] >= 0) { 831da177e4SLinus Torvalds sprintf(card->longname + strlen(card->longname), "irq %d", irq[dev]); 841da177e4SLinus Torvalds } else { 851da177e4SLinus Torvalds strcat(card->longname, "polled"); 861da177e4SLinus Torvalds } 871da177e4SLinus Torvalds 88c1099fcbSClemens Ladisch err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, port[dev], 0, 89dba8b469SClemens Ladisch irq[dev], NULL); 908f7ba051STakashi Iwai if (err < 0) { 911da177e4SLinus Torvalds printk(KERN_ERR "MPU401 not detected at 0x%lx\n", port[dev]); 9216dab54bSTakashi Iwai goto _err; 931da177e4SLinus Torvalds } 9416dab54bSTakashi Iwai 951da177e4SLinus Torvalds *rcard = card; 961da177e4SLinus Torvalds return 0; 9716dab54bSTakashi Iwai 9816dab54bSTakashi Iwai _err: 9916dab54bSTakashi Iwai snd_card_free(card); 10016dab54bSTakashi Iwai return err; 1011da177e4SLinus Torvalds } 1021da177e4SLinus Torvalds 103b3fe9512STakashi Iwai static int __devinit snd_mpu401_probe(struct platform_device *devptr) 1041da177e4SLinus Torvalds { 105b3fe9512STakashi Iwai int dev = devptr->id; 106b3fe9512STakashi Iwai int err; 107b3fe9512STakashi Iwai struct snd_card *card; 108b3fe9512STakashi Iwai 1091da177e4SLinus Torvalds if (port[dev] == SNDRV_AUTO_PORT) { 1101da177e4SLinus Torvalds snd_printk(KERN_ERR "specify port\n"); 1111da177e4SLinus Torvalds return -EINVAL; 1121da177e4SLinus Torvalds } 1131da177e4SLinus Torvalds if (irq[dev] == SNDRV_AUTO_IRQ) { 1141da177e4SLinus Torvalds snd_printk(KERN_ERR "specify or disable IRQ\n"); 1151da177e4SLinus Torvalds return -EINVAL; 1161da177e4SLinus Torvalds } 117b3fe9512STakashi Iwai err = snd_mpu401_create(dev, &card); 118b3fe9512STakashi Iwai if (err < 0) 119b3fe9512STakashi Iwai return err; 120b3fe9512STakashi Iwai snd_card_set_dev(card, &devptr->dev); 121b3fe9512STakashi Iwai if ((err = snd_card_register(card)) < 0) { 122b3fe9512STakashi Iwai snd_card_free(card); 123b3fe9512STakashi Iwai return err; 1241da177e4SLinus Torvalds } 125b3fe9512STakashi Iwai platform_set_drvdata(devptr, card); 126b3fe9512STakashi Iwai return 0; 127b3fe9512STakashi Iwai } 128b3fe9512STakashi Iwai 129b3fe9512STakashi Iwai static int __devexit snd_mpu401_remove(struct platform_device *devptr) 130b3fe9512STakashi Iwai { 131b3fe9512STakashi Iwai snd_card_free(platform_get_drvdata(devptr)); 132b3fe9512STakashi Iwai platform_set_drvdata(devptr, NULL); 133b3fe9512STakashi Iwai return 0; 134b3fe9512STakashi Iwai } 135b3fe9512STakashi Iwai 136b3fe9512STakashi Iwai #define SND_MPU401_DRIVER "snd_mpu401" 137b3fe9512STakashi Iwai 138b3fe9512STakashi Iwai static struct platform_driver snd_mpu401_driver = { 139b3fe9512STakashi Iwai .probe = snd_mpu401_probe, 140b3fe9512STakashi Iwai .remove = __devexit_p(snd_mpu401_remove), 141b3fe9512STakashi Iwai .driver = { 142b3fe9512STakashi Iwai .name = SND_MPU401_DRIVER 143b3fe9512STakashi Iwai }, 144b3fe9512STakashi Iwai }; 145b3fe9512STakashi Iwai 1461da177e4SLinus Torvalds 1471da177e4SLinus Torvalds #ifdef CONFIG_PNP 1481da177e4SLinus Torvalds 1491da177e4SLinus Torvalds #define IO_EXTENT 2 1501da177e4SLinus Torvalds 1511da177e4SLinus Torvalds static struct pnp_device_id snd_mpu401_pnpids[] = { 1521da177e4SLinus Torvalds { .id = "PNPb006" }, 1531da177e4SLinus Torvalds { .id = "" } 1541da177e4SLinus Torvalds }; 1551da177e4SLinus Torvalds 1561da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pnp, snd_mpu401_pnpids); 1571da177e4SLinus Torvalds 158fad43488SAndrew Morton static int __devinit snd_mpu401_pnp(int dev, struct pnp_dev *device, 1591da177e4SLinus Torvalds const struct pnp_device_id *id) 1601da177e4SLinus Torvalds { 1611da177e4SLinus Torvalds if (!pnp_port_valid(device, 0) || 1621da177e4SLinus Torvalds pnp_port_flags(device, 0) & IORESOURCE_DISABLED) { 1631da177e4SLinus Torvalds snd_printk(KERN_ERR "no PnP port\n"); 1641da177e4SLinus Torvalds return -ENODEV; 1651da177e4SLinus Torvalds } 1661da177e4SLinus Torvalds if (pnp_port_len(device, 0) < IO_EXTENT) { 167aa0a2ddcSGreg Kroah-Hartman snd_printk(KERN_ERR "PnP port length is %llu, expected %d\n", 168aa0a2ddcSGreg Kroah-Hartman (unsigned long long)pnp_port_len(device, 0), 169aa0a2ddcSGreg Kroah-Hartman IO_EXTENT); 1701da177e4SLinus Torvalds return -ENODEV; 1711da177e4SLinus Torvalds } 1721da177e4SLinus Torvalds port[dev] = pnp_port_start(device, 0); 1731da177e4SLinus Torvalds 1741da177e4SLinus Torvalds if (!pnp_irq_valid(device, 0) || 1751da177e4SLinus Torvalds pnp_irq_flags(device, 0) & IORESOURCE_DISABLED) { 1761da177e4SLinus Torvalds snd_printk(KERN_WARNING "no PnP irq, using polling\n"); 1771da177e4SLinus Torvalds irq[dev] = -1; 1781da177e4SLinus Torvalds } else { 1791da177e4SLinus Torvalds irq[dev] = pnp_irq(device, 0); 1801da177e4SLinus Torvalds } 1811da177e4SLinus Torvalds return 0; 1821da177e4SLinus Torvalds } 1831da177e4SLinus Torvalds 1841da177e4SLinus Torvalds static int __devinit snd_mpu401_pnp_probe(struct pnp_dev *pnp_dev, 1851da177e4SLinus Torvalds const struct pnp_device_id *id) 1861da177e4SLinus Torvalds { 1871da177e4SLinus Torvalds static int dev; 188e1fad17bSTakashi Iwai struct snd_card *card; 1891da177e4SLinus Torvalds int err; 1901da177e4SLinus Torvalds 1911da177e4SLinus Torvalds for ( ; dev < SNDRV_CARDS; ++dev) { 1921da177e4SLinus Torvalds if (!enable[dev] || !pnp[dev]) 1931da177e4SLinus Torvalds continue; 1941da177e4SLinus Torvalds err = snd_mpu401_pnp(dev, pnp_dev, id); 1951da177e4SLinus Torvalds if (err < 0) 1961da177e4SLinus Torvalds return err; 1971da177e4SLinus Torvalds err = snd_mpu401_create(dev, &card); 1981da177e4SLinus Torvalds if (err < 0) 1991da177e4SLinus Torvalds return err; 200b3fe9512STakashi Iwai if ((err = snd_card_register(card)) < 0) { 201b3fe9512STakashi Iwai snd_card_free(card); 202b3fe9512STakashi Iwai return err; 203b3fe9512STakashi Iwai } 2041da177e4SLinus Torvalds snd_card_set_dev(card, &pnp_dev->dev); 2051da177e4SLinus Torvalds pnp_set_drvdata(pnp_dev, card); 206f301ae6aSBjorn Helgaas snd_mpu401_devices++; 2071da177e4SLinus Torvalds ++dev; 2081da177e4SLinus Torvalds return 0; 2091da177e4SLinus Torvalds } 2101da177e4SLinus Torvalds return -ENODEV; 2111da177e4SLinus Torvalds } 2121da177e4SLinus Torvalds 2131da177e4SLinus Torvalds static void __devexit snd_mpu401_pnp_remove(struct pnp_dev *dev) 2141da177e4SLinus Torvalds { 215e1fad17bSTakashi Iwai struct snd_card *card = (struct snd_card *) pnp_get_drvdata(dev); 2161da177e4SLinus Torvalds 2171da177e4SLinus Torvalds snd_card_disconnect(card); 2182b29b13cSTakashi Iwai snd_card_free_when_closed(card); 2191da177e4SLinus Torvalds } 2201da177e4SLinus Torvalds 2211da177e4SLinus Torvalds static struct pnp_driver snd_mpu401_pnp_driver = { 2221da177e4SLinus Torvalds .name = "mpu401", 2231da177e4SLinus Torvalds .id_table = snd_mpu401_pnpids, 2241da177e4SLinus Torvalds .probe = snd_mpu401_pnp_probe, 2251da177e4SLinus Torvalds .remove = __devexit_p(snd_mpu401_pnp_remove), 2261da177e4SLinus Torvalds }; 2271da177e4SLinus Torvalds #else 2281da177e4SLinus Torvalds static struct pnp_driver snd_mpu401_pnp_driver; 2291da177e4SLinus Torvalds #endif 2301da177e4SLinus Torvalds 231c12aad6eSRandy Dunlap static void snd_mpu401_unregister_all(void) 232f7a9275dSClemens Ladisch { 233f7a9275dSClemens Ladisch int i; 234f7a9275dSClemens Ladisch 235f7a9275dSClemens Ladisch if (pnp_registered) 236f7a9275dSClemens Ladisch pnp_unregister_driver(&snd_mpu401_pnp_driver); 237f7a9275dSClemens Ladisch for (i = 0; i < ARRAY_SIZE(platform_devices); ++i) 238f7a9275dSClemens Ladisch platform_device_unregister(platform_devices[i]); 239f7a9275dSClemens Ladisch platform_driver_unregister(&snd_mpu401_driver); 240f7a9275dSClemens Ladisch } 241f7a9275dSClemens Ladisch 2421da177e4SLinus Torvalds static int __init alsa_card_mpu401_init(void) 2431da177e4SLinus Torvalds { 244f301ae6aSBjorn Helgaas int i, err; 2451da177e4SLinus Torvalds 246b3fe9512STakashi Iwai if ((err = platform_driver_register(&snd_mpu401_driver)) < 0) 247b3fe9512STakashi Iwai return err; 248b3fe9512STakashi Iwai 2498278ca8fSTakashi Iwai for (i = 0; i < SNDRV_CARDS; i++) { 250b3fe9512STakashi Iwai struct platform_device *device; 2518278ca8fSTakashi Iwai if (! enable[i]) 2528278ca8fSTakashi Iwai continue; 2531da177e4SLinus Torvalds #ifdef CONFIG_PNP 254b3fe9512STakashi Iwai if (pnp[i]) 2551da177e4SLinus Torvalds continue; 2561da177e4SLinus Torvalds #endif 257b3fe9512STakashi Iwai device = platform_device_register_simple(SND_MPU401_DRIVER, 258b3fe9512STakashi Iwai i, NULL, 0); 259a182ee98SRene Herman if (IS_ERR(device)) 260a182ee98SRene Herman continue; 2617152447dSRene Herman if (!platform_get_drvdata(device)) { 2627152447dSRene Herman platform_device_unregister(device); 2637152447dSRene Herman continue; 2647152447dSRene Herman } 265f7a9275dSClemens Ladisch platform_devices[i] = device; 266f301ae6aSBjorn Helgaas snd_mpu401_devices++; 2671da177e4SLinus Torvalds } 268f301ae6aSBjorn Helgaas err = pnp_register_driver(&snd_mpu401_pnp_driver); 269f301ae6aSBjorn Helgaas if (!err) 2701da177e4SLinus Torvalds pnp_registered = 1; 2711da177e4SLinus Torvalds 272f301ae6aSBjorn Helgaas if (!snd_mpu401_devices) { 2731da177e4SLinus Torvalds #ifdef MODULE 2741da177e4SLinus Torvalds printk(KERN_ERR "MPU-401 device not found or device busy\n"); 2751da177e4SLinus Torvalds #endif 276a182ee98SRene Herman snd_mpu401_unregister_all(); 277a182ee98SRene Herman return -ENODEV; 2781da177e4SLinus Torvalds } 2791da177e4SLinus Torvalds return 0; 2801da177e4SLinus Torvalds } 2811da177e4SLinus Torvalds 2821da177e4SLinus Torvalds static void __exit alsa_card_mpu401_exit(void) 2831da177e4SLinus Torvalds { 284f7a9275dSClemens Ladisch snd_mpu401_unregister_all(); 2851da177e4SLinus Torvalds } 2861da177e4SLinus Torvalds 2871da177e4SLinus Torvalds module_init(alsa_card_mpu401_init) 2881da177e4SLinus Torvalds module_exit(alsa_card_mpu401_exit) 289