1f9933487SOndrej Zary /* 2f9933487SOndrej Zary * Driver for C-Media CMI8328-based soundcards, such as AudioExcel AV500 3f9933487SOndrej Zary * Copyright (c) 2012 Ondrej Zary 4f9933487SOndrej Zary * 5f9933487SOndrej Zary * AudioExcel AV500 card consists of: 6f9933487SOndrej Zary * - CMI8328 - main chip (SB Pro emulation, gameport, OPL3, MPU401, CD-ROM) 7f9933487SOndrej Zary * - CS4231A - WSS codec 8f9933487SOndrej Zary * - Dream SAM9233+GMS950400+RAM+ROM: Wavetable MIDI, connected to MPU401 9f9933487SOndrej Zary */ 10f9933487SOndrej Zary 11f9933487SOndrej Zary #include <linux/init.h> 12f9933487SOndrej Zary #include <linux/isa.h> 13f9933487SOndrej Zary #include <linux/module.h> 14f9933487SOndrej Zary #include <linux/gameport.h> 15f9933487SOndrej Zary #include <asm/dma.h> 16f9933487SOndrej Zary #include <sound/core.h> 17f9933487SOndrej Zary #include <sound/wss.h> 18f9933487SOndrej Zary #include <sound/opl3.h> 19f9933487SOndrej Zary #include <sound/mpu401.h> 20f9933487SOndrej Zary #define SNDRV_LEGACY_FIND_FREE_IOPORT 21f9933487SOndrej Zary #define SNDRV_LEGACY_FIND_FREE_IRQ 22f9933487SOndrej Zary #define SNDRV_LEGACY_FIND_FREE_DMA 23f9933487SOndrej Zary #include <sound/initval.h> 24f9933487SOndrej Zary 25f9933487SOndrej Zary MODULE_AUTHOR("Ondrej Zary <linux@rainbow-software.org>"); 26f9933487SOndrej Zary MODULE_DESCRIPTION("C-Media CMI8328"); 27f9933487SOndrej Zary MODULE_LICENSE("GPL"); 28f9933487SOndrej Zary 29f9933487SOndrej Zary #if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) 30f9933487SOndrej Zary #define SUPPORT_JOYSTICK 1 31f9933487SOndrej Zary #endif 32f9933487SOndrej Zary 33f9933487SOndrej Zary /* I/O port is configured by jumpers on the card to one of these */ 34f9933487SOndrej Zary static int cmi8328_ports[] = { 0x530, 0xe80, 0xf40, 0x604 }; 35f9933487SOndrej Zary #define CMI8328_MAX ARRAY_SIZE(cmi8328_ports) 36f9933487SOndrej Zary 37f9933487SOndrej Zary static int index[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = -1}; 38f9933487SOndrej Zary static char *id[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = NULL}; 39f9933487SOndrej Zary static long port[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = SNDRV_AUTO_PORT}; 40f9933487SOndrej Zary static int irq[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = SNDRV_AUTO_IRQ}; 41f9933487SOndrej Zary static int dma1[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = SNDRV_AUTO_DMA}; 42f9933487SOndrej Zary static int dma2[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = SNDRV_AUTO_DMA}; 43f9933487SOndrej Zary static long mpuport[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = SNDRV_AUTO_PORT}; 44f9933487SOndrej Zary static int mpuirq[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = SNDRV_AUTO_IRQ}; 45f9933487SOndrej Zary #ifdef SUPPORT_JOYSTICK 46f9933487SOndrej Zary static bool gameport[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = true}; 47f9933487SOndrej Zary #endif 48f9933487SOndrej Zary 49f9933487SOndrej Zary module_param_array(index, int, NULL, 0444); 50f9933487SOndrej Zary MODULE_PARM_DESC(index, "Index value for CMI8328 soundcard."); 51f9933487SOndrej Zary module_param_array(id, charp, NULL, 0444); 52f9933487SOndrej Zary MODULE_PARM_DESC(id, "ID string for CMI8328 soundcard."); 53f9933487SOndrej Zary 54f9933487SOndrej Zary module_param_array(port, long, NULL, 0444); 55f9933487SOndrej Zary MODULE_PARM_DESC(port, "Port # for CMI8328 driver."); 56f9933487SOndrej Zary module_param_array(irq, int, NULL, 0444); 57f9933487SOndrej Zary MODULE_PARM_DESC(irq, "IRQ # for CMI8328 driver."); 58f9933487SOndrej Zary module_param_array(dma1, int, NULL, 0444); 59f9933487SOndrej Zary MODULE_PARM_DESC(dma1, "DMA1 for CMI8328 driver."); 60f9933487SOndrej Zary module_param_array(dma2, int, NULL, 0444); 61f9933487SOndrej Zary MODULE_PARM_DESC(dma2, "DMA2 for CMI8328 driver."); 62f9933487SOndrej Zary 63f9933487SOndrej Zary module_param_array(mpuport, long, NULL, 0444); 64f9933487SOndrej Zary MODULE_PARM_DESC(mpuport, "MPU-401 port # for CMI8328 driver."); 65f9933487SOndrej Zary module_param_array(mpuirq, int, NULL, 0444); 66f9933487SOndrej Zary MODULE_PARM_DESC(mpuirq, "IRQ # for CMI8328 MPU-401 port."); 67f9933487SOndrej Zary #ifdef SUPPORT_JOYSTICK 68f9933487SOndrej Zary module_param_array(gameport, bool, NULL, 0444); 69f9933487SOndrej Zary MODULE_PARM_DESC(gameport, "Enable gameport."); 70f9933487SOndrej Zary #endif 71f9933487SOndrej Zary 72f9933487SOndrej Zary struct snd_cmi8328 { 73f9933487SOndrej Zary u16 port; 74f9933487SOndrej Zary u8 cfg[3]; 75f9933487SOndrej Zary u8 wss_cfg; 76f9933487SOndrej Zary struct snd_card *card; 77f9933487SOndrej Zary struct snd_wss *wss; 78f9933487SOndrej Zary #ifdef SUPPORT_JOYSTICK 79f9933487SOndrej Zary struct gameport *gameport; 80f9933487SOndrej Zary #endif 81f9933487SOndrej Zary }; 82f9933487SOndrej Zary 83f9933487SOndrej Zary /* CMI8328 configuration registers */ 84f9933487SOndrej Zary #define CFG1 0x61 85f9933487SOndrej Zary #define CFG1_SB_DISABLE (1 << 0) 86f9933487SOndrej Zary #define CFG1_GAMEPORT (1 << 1) 87f9933487SOndrej Zary /* 88f9933487SOndrej Zary * bit 0: SB: 0=enabled, 1=disabled 89f9933487SOndrej Zary * bit 1: gameport: 0=disabled, 1=enabled 90f9933487SOndrej Zary * bits 2-4: SB IRQ: 001=3, 010=5, 011=7, 100=9, 101=10, 110=11 91f9933487SOndrej Zary * bits 5-6: SB DMA: 00=disabled (when SB disabled), 01=DMA0, 10=DMA1, 11=DMA3 92f9933487SOndrej Zary * bit 7: SB port: 0=0x220, 1=0x240 93f9933487SOndrej Zary */ 94f9933487SOndrej Zary #define CFG2 0x62 95f9933487SOndrej Zary #define CFG2_MPU_ENABLE (1 << 2) 96f9933487SOndrej Zary /* 97f9933487SOndrej Zary * bits 0-1: CD-ROM mode: 00=disabled, 01=Panasonic, 10=Sony/Mitsumi/Wearnes, 98f9933487SOndrej Zary 11=IDE 99f9933487SOndrej Zary * bit 2: MPU401: 0=disabled, 1=enabled 100f9933487SOndrej Zary * bits 3-4: MPU401 IRQ: 00=3, 01=5, 10=7, 11=9, 101f9933487SOndrej Zary * bits 5-7: MPU401 port: 000=0x300, 001=0x310, 010=0x320, 011=0x330, 100=0x332, 102f9933487SOndrej Zary 101=0x334, 110=0x336 103f9933487SOndrej Zary */ 104f9933487SOndrej Zary #define CFG3 0x63 105f9933487SOndrej Zary /* 106f9933487SOndrej Zary * bits 0-2: CD-ROM IRQ: 000=disabled, 001=3, 010=5, 011=7, 100=9, 101=10, 107f9933487SOndrej Zary 110=11 108f9933487SOndrej Zary * bits 3-4: CD-ROM DMA: 00=disabled, 01=DMA0, 10=DMA1, 11=DMA3 109f9933487SOndrej Zary * bits 5-7: CD-ROM port: 000=0x300, 001=0x310, 010=0x320, 011=0x330, 100=0x340, 110f9933487SOndrej Zary 101=0x350, 110=0x360, 111=0x370 111f9933487SOndrej Zary */ 112f9933487SOndrej Zary 113f9933487SOndrej Zary static u8 snd_cmi8328_cfg_read(u16 port, u8 reg) 114f9933487SOndrej Zary { 115f9933487SOndrej Zary outb(0x43, port + 3); 116f9933487SOndrej Zary outb(0x21, port + 3); 117f9933487SOndrej Zary outb(reg, port + 3); 118f9933487SOndrej Zary return inb(port); 119f9933487SOndrej Zary } 120f9933487SOndrej Zary 121f9933487SOndrej Zary static void snd_cmi8328_cfg_write(u16 port, u8 reg, u8 val) 122f9933487SOndrej Zary { 123f9933487SOndrej Zary outb(0x43, port + 3); 124f9933487SOndrej Zary outb(0x21, port + 3); 125f9933487SOndrej Zary outb(reg, port + 3); 126f9933487SOndrej Zary outb(val, port + 3); /* yes, value goes to the same port as index */ 127f9933487SOndrej Zary } 128f9933487SOndrej Zary 12977ed16ccSTakashi Iwai #ifdef CONFIG_PM 130f9933487SOndrej Zary static void snd_cmi8328_cfg_save(u16 port, u8 cfg[]) 131f9933487SOndrej Zary { 132f9933487SOndrej Zary cfg[0] = snd_cmi8328_cfg_read(port, CFG1); 133f9933487SOndrej Zary cfg[1] = snd_cmi8328_cfg_read(port, CFG2); 134f9933487SOndrej Zary cfg[2] = snd_cmi8328_cfg_read(port, CFG3); 135f9933487SOndrej Zary } 136f9933487SOndrej Zary 137f9933487SOndrej Zary static void snd_cmi8328_cfg_restore(u16 port, u8 cfg[]) 138f9933487SOndrej Zary { 139f9933487SOndrej Zary snd_cmi8328_cfg_write(port, CFG1, cfg[0]); 140f9933487SOndrej Zary snd_cmi8328_cfg_write(port, CFG2, cfg[1]); 141f9933487SOndrej Zary snd_cmi8328_cfg_write(port, CFG3, cfg[2]); 142f9933487SOndrej Zary } 14377ed16ccSTakashi Iwai #endif /* CONFIG_PM */ 144f9933487SOndrej Zary 1451bff292eSBill Pemberton static int snd_cmi8328_mixer(struct snd_wss *chip) 146f9933487SOndrej Zary { 147f9933487SOndrej Zary struct snd_card *card; 148f9933487SOndrej Zary struct snd_ctl_elem_id id1, id2; 149f9933487SOndrej Zary int err; 150f9933487SOndrej Zary 151f9933487SOndrej Zary card = chip->card; 152f9933487SOndrej Zary 153f9933487SOndrej Zary memset(&id1, 0, sizeof(id1)); 154f9933487SOndrej Zary memset(&id2, 0, sizeof(id2)); 155f9933487SOndrej Zary id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 156f9933487SOndrej Zary /* rename AUX0 switch to CD */ 157f9933487SOndrej Zary strcpy(id1.name, "Aux Playback Switch"); 158f9933487SOndrej Zary strcpy(id2.name, "CD Playback Switch"); 159f9933487SOndrej Zary err = snd_ctl_rename_id(card, &id1, &id2); 160f9933487SOndrej Zary if (err < 0) { 161f9933487SOndrej Zary snd_printk(KERN_ERR "error renaming control\n"); 162f9933487SOndrej Zary return err; 163f9933487SOndrej Zary } 164f9933487SOndrej Zary /* rename AUX0 volume to CD */ 165f9933487SOndrej Zary strcpy(id1.name, "Aux Playback Volume"); 166f9933487SOndrej Zary strcpy(id2.name, "CD Playback Volume"); 167f9933487SOndrej Zary err = snd_ctl_rename_id(card, &id1, &id2); 168f9933487SOndrej Zary if (err < 0) { 169f9933487SOndrej Zary snd_printk(KERN_ERR "error renaming control\n"); 170f9933487SOndrej Zary return err; 171f9933487SOndrej Zary } 172f9933487SOndrej Zary /* rename AUX1 switch to Synth */ 173f9933487SOndrej Zary strcpy(id1.name, "Aux Playback Switch"); 174f9933487SOndrej Zary id1.index = 1; 175f9933487SOndrej Zary strcpy(id2.name, "Synth Playback Switch"); 176f9933487SOndrej Zary err = snd_ctl_rename_id(card, &id1, &id2); 177f9933487SOndrej Zary if (err < 0) { 178f9933487SOndrej Zary snd_printk(KERN_ERR "error renaming control\n"); 179f9933487SOndrej Zary return err; 180f9933487SOndrej Zary } 181f9933487SOndrej Zary /* rename AUX1 volume to Synth */ 182f9933487SOndrej Zary strcpy(id1.name, "Aux Playback Volume"); 183f9933487SOndrej Zary id1.index = 1; 184f9933487SOndrej Zary strcpy(id2.name, "Synth Playback Volume"); 185f9933487SOndrej Zary err = snd_ctl_rename_id(card, &id1, &id2); 186f9933487SOndrej Zary if (err < 0) { 187f9933487SOndrej Zary snd_printk(KERN_ERR "error renaming control\n"); 188f9933487SOndrej Zary return err; 189f9933487SOndrej Zary } 190f9933487SOndrej Zary 191f9933487SOndrej Zary return 0; 192f9933487SOndrej Zary } 193f9933487SOndrej Zary 194f9933487SOndrej Zary /* find index of an item in "-1"-ended array */ 195f9933487SOndrej Zary int array_find(int array[], int item) 196f9933487SOndrej Zary { 197f9933487SOndrej Zary int i; 198f9933487SOndrej Zary 199f9933487SOndrej Zary for (i = 0; array[i] != -1; i++) 200f9933487SOndrej Zary if (array[i] == item) 201f9933487SOndrej Zary return i; 202f9933487SOndrej Zary 203f9933487SOndrej Zary return -1; 204f9933487SOndrej Zary } 205f9933487SOndrej Zary /* the same for long */ 206f9933487SOndrej Zary int array_find_l(long array[], long item) 207f9933487SOndrej Zary { 208f9933487SOndrej Zary int i; 209f9933487SOndrej Zary 210f9933487SOndrej Zary for (i = 0; array[i] != -1; i++) 211f9933487SOndrej Zary if (array[i] == item) 212f9933487SOndrej Zary return i; 213f9933487SOndrej Zary 214f9933487SOndrej Zary return -1; 215f9933487SOndrej Zary } 216f9933487SOndrej Zary 2171bff292eSBill Pemberton static int snd_cmi8328_probe(struct device *pdev, unsigned int ndev) 218f9933487SOndrej Zary { 219f9933487SOndrej Zary struct snd_card *card; 220f9933487SOndrej Zary struct snd_opl3 *opl3; 221f9933487SOndrej Zary struct snd_cmi8328 *cmi; 222f9933487SOndrej Zary #ifdef SUPPORT_JOYSTICK 223f9933487SOndrej Zary struct resource *res; 224f9933487SOndrej Zary #endif 225f9933487SOndrej Zary int err, pos; 226f9933487SOndrej Zary static long mpu_ports[] = { 0x330, 0x300, 0x310, 0x320, 0x332, 0x334, 227f9933487SOndrej Zary 0x336, -1 }; 228f9933487SOndrej Zary static u8 mpu_port_bits[] = { 3, 0, 1, 2, 4, 5, 6 }; 229f9933487SOndrej Zary static int mpu_irqs[] = { 9, 7, 5, 3, -1 }; 230f9933487SOndrej Zary static u8 mpu_irq_bits[] = { 3, 2, 1, 0 }; 231f9933487SOndrej Zary static int irqs[] = { 9, 10, 11, 7, -1 }; 232f9933487SOndrej Zary static u8 irq_bits[] = { 2, 3, 4, 1 }; 233f9933487SOndrej Zary static int dma1s[] = { 3, 1, 0, -1 }; 234f9933487SOndrej Zary static u8 dma_bits[] = { 3, 2, 1 }; 235f9933487SOndrej Zary static int dma2s[][2] = { {1, -1}, {0, -1}, {-1, -1}, {0, -1} }; 236f9933487SOndrej Zary u16 port = cmi8328_ports[ndev]; 237f9933487SOndrej Zary u8 val; 238f9933487SOndrej Zary 239f9933487SOndrej Zary /* 0xff is invalid configuration (but settable - hope it isn't set) */ 240f9933487SOndrej Zary if (snd_cmi8328_cfg_read(port, CFG1) == 0xff) 241f9933487SOndrej Zary return -ENODEV; 242f9933487SOndrej Zary /* the SB disable bit must NEVER EVER be cleared or the WSS dies */ 243f9933487SOndrej Zary snd_cmi8328_cfg_write(port, CFG1, CFG1_SB_DISABLE); 244f9933487SOndrej Zary if (snd_cmi8328_cfg_read(port, CFG1) != CFG1_SB_DISABLE) 245f9933487SOndrej Zary return -ENODEV; 246f9933487SOndrej Zary /* disable everything first */ 247f9933487SOndrej Zary snd_cmi8328_cfg_write(port, CFG2, 0); /* disable CDROM and MPU401 */ 248f9933487SOndrej Zary snd_cmi8328_cfg_write(port, CFG3, 0); /* disable CDROM IRQ and DMA */ 249f9933487SOndrej Zary 250f9933487SOndrej Zary if (irq[ndev] == SNDRV_AUTO_IRQ) { 251f9933487SOndrej Zary irq[ndev] = snd_legacy_find_free_irq(irqs); 252f9933487SOndrej Zary if (irq[ndev] < 0) { 253f9933487SOndrej Zary snd_printk(KERN_ERR "unable to find a free IRQ\n"); 254f9933487SOndrej Zary return -EBUSY; 255f9933487SOndrej Zary } 256f9933487SOndrej Zary } 257f9933487SOndrej Zary if (dma1[ndev] == SNDRV_AUTO_DMA) { 258f9933487SOndrej Zary dma1[ndev] = snd_legacy_find_free_dma(dma1s); 259f9933487SOndrej Zary if (dma1[ndev] < 0) { 260f9933487SOndrej Zary snd_printk(KERN_ERR "unable to find a free DMA1\n"); 261f9933487SOndrej Zary return -EBUSY; 262f9933487SOndrej Zary } 263f9933487SOndrej Zary } 264f9933487SOndrej Zary if (dma2[ndev] == SNDRV_AUTO_DMA) { 265f9933487SOndrej Zary dma2[ndev] = snd_legacy_find_free_dma(dma2s[dma1[ndev] % 4]); 266f9933487SOndrej Zary if (dma2[ndev] < 0) { 267f9933487SOndrej Zary snd_printk(KERN_WARNING "unable to find a free DMA2, full-duplex will not work\n"); 268f9933487SOndrej Zary dma2[ndev] = -1; 269f9933487SOndrej Zary } 270f9933487SOndrej Zary } 271f9933487SOndrej Zary /* configure WSS IRQ... */ 272f9933487SOndrej Zary pos = array_find(irqs, irq[ndev]); 273f9933487SOndrej Zary if (pos < 0) { 274f9933487SOndrej Zary snd_printk(KERN_ERR "invalid IRQ %d\n", irq[ndev]); 275f9933487SOndrej Zary return -EINVAL; 276f9933487SOndrej Zary } 277f9933487SOndrej Zary val = irq_bits[pos] << 3; 278f9933487SOndrej Zary /* ...and DMA... */ 279f9933487SOndrej Zary pos = array_find(dma1s, dma1[ndev]); 280f9933487SOndrej Zary if (pos < 0) { 281f9933487SOndrej Zary snd_printk(KERN_ERR "invalid DMA1 %d\n", dma1[ndev]); 282f9933487SOndrej Zary return -EINVAL; 283f9933487SOndrej Zary } 284f9933487SOndrej Zary val |= dma_bits[pos]; 285f9933487SOndrej Zary /* ...and DMA2 */ 286f9933487SOndrej Zary if (dma2[ndev] >= 0 && dma1[ndev] != dma2[ndev]) { 287f9933487SOndrej Zary pos = array_find(dma2s[dma1[ndev]], dma2[ndev]); 288f9933487SOndrej Zary if (pos < 0) { 289f9933487SOndrej Zary snd_printk(KERN_ERR "invalid DMA2 %d\n", dma2[ndev]); 290f9933487SOndrej Zary return -EINVAL; 291f9933487SOndrej Zary } 292f9933487SOndrej Zary val |= 0x04; /* enable separate capture DMA */ 293f9933487SOndrej Zary } 294f9933487SOndrej Zary outb(val, port); 295f9933487SOndrej Zary 2964323cc4dSTakashi Iwai err = snd_card_new(pdev, index[ndev], id[ndev], THIS_MODULE, 297f9933487SOndrej Zary sizeof(struct snd_cmi8328), &card); 298f9933487SOndrej Zary if (err < 0) 299f9933487SOndrej Zary return err; 300f9933487SOndrej Zary cmi = card->private_data; 301f9933487SOndrej Zary cmi->card = card; 302f9933487SOndrej Zary cmi->port = port; 303f9933487SOndrej Zary cmi->wss_cfg = val; 304f9933487SOndrej Zary 305f9933487SOndrej Zary err = snd_wss_create(card, port + 4, -1, irq[ndev], dma1[ndev], 306f9933487SOndrej Zary dma2[ndev], WSS_HW_DETECT, 0, &cmi->wss); 307f9933487SOndrej Zary if (err < 0) 308f9933487SOndrej Zary goto error; 309f9933487SOndrej Zary 310f9933487SOndrej Zary err = snd_wss_pcm(cmi->wss, 0, NULL); 311f9933487SOndrej Zary if (err < 0) 312f9933487SOndrej Zary goto error; 313f9933487SOndrej Zary 314f9933487SOndrej Zary err = snd_wss_mixer(cmi->wss); 315f9933487SOndrej Zary if (err < 0) 316f9933487SOndrej Zary goto error; 317f9933487SOndrej Zary err = snd_cmi8328_mixer(cmi->wss); 318f9933487SOndrej Zary if (err < 0) 319f9933487SOndrej Zary goto error; 320f9933487SOndrej Zary 321f9933487SOndrej Zary if (snd_wss_timer(cmi->wss, 0, NULL) < 0) 322f9933487SOndrej Zary snd_printk(KERN_WARNING "error initializing WSS timer\n"); 323f9933487SOndrej Zary 324f9933487SOndrej Zary if (mpuport[ndev] == SNDRV_AUTO_PORT) { 325f9933487SOndrej Zary mpuport[ndev] = snd_legacy_find_free_ioport(mpu_ports, 2); 326f9933487SOndrej Zary if (mpuport[ndev] < 0) 327f9933487SOndrej Zary snd_printk(KERN_ERR "unable to find a free MPU401 port\n"); 328f9933487SOndrej Zary } 329f9933487SOndrej Zary if (mpuirq[ndev] == SNDRV_AUTO_IRQ) { 330f9933487SOndrej Zary mpuirq[ndev] = snd_legacy_find_free_irq(mpu_irqs); 331f9933487SOndrej Zary if (mpuirq[ndev] < 0) 332f9933487SOndrej Zary snd_printk(KERN_ERR "unable to find a free MPU401 IRQ\n"); 333f9933487SOndrej Zary } 334f9933487SOndrej Zary /* enable and configure MPU401 */ 335f9933487SOndrej Zary if (mpuport[ndev] > 0 && mpuirq[ndev] > 0) { 336f9933487SOndrej Zary val = CFG2_MPU_ENABLE; 337f9933487SOndrej Zary pos = array_find_l(mpu_ports, mpuport[ndev]); 338f9933487SOndrej Zary if (pos < 0) 339f9933487SOndrej Zary snd_printk(KERN_WARNING "invalid MPU401 port 0x%lx\n", 340f9933487SOndrej Zary mpuport[ndev]); 341f9933487SOndrej Zary else { 342f9933487SOndrej Zary val |= mpu_port_bits[pos] << 5; 343f9933487SOndrej Zary pos = array_find(mpu_irqs, mpuirq[ndev]); 344f9933487SOndrej Zary if (pos < 0) 345f9933487SOndrej Zary snd_printk(KERN_WARNING "invalid MPU401 IRQ %d\n", 346f9933487SOndrej Zary mpuirq[ndev]); 347f9933487SOndrej Zary else { 348f9933487SOndrej Zary val |= mpu_irq_bits[pos] << 3; 349f9933487SOndrej Zary snd_cmi8328_cfg_write(port, CFG2, val); 350f9933487SOndrej Zary if (snd_mpu401_uart_new(card, 0, 351f9933487SOndrej Zary MPU401_HW_MPU401, mpuport[ndev], 352f9933487SOndrej Zary 0, mpuirq[ndev], NULL) < 0) 353f9933487SOndrej Zary snd_printk(KERN_ERR "error initializing MPU401\n"); 354f9933487SOndrej Zary } 355f9933487SOndrej Zary } 356f9933487SOndrej Zary } 357f9933487SOndrej Zary /* OPL3 is hardwired to 0x388 and cannot be disabled */ 358f9933487SOndrej Zary if (snd_opl3_create(card, 0x388, 0x38a, OPL3_HW_AUTO, 0, &opl3) < 0) 359f9933487SOndrej Zary snd_printk(KERN_ERR "error initializing OPL3\n"); 360f9933487SOndrej Zary else 361f9933487SOndrej Zary if (snd_opl3_hwdep_new(opl3, 0, 1, NULL) < 0) 362f9933487SOndrej Zary snd_printk(KERN_WARNING "error initializing OPL3 hwdep\n"); 363f9933487SOndrej Zary 364f9933487SOndrej Zary strcpy(card->driver, "CMI8328"); 365f9933487SOndrej Zary strcpy(card->shortname, "C-Media CMI8328"); 366f9933487SOndrej Zary sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d,%d", 367f9933487SOndrej Zary card->shortname, cmi->wss->port, irq[ndev], dma1[ndev], 368f9933487SOndrej Zary (dma2[ndev] >= 0) ? dma2[ndev] : dma1[ndev]); 369f9933487SOndrej Zary 370f9933487SOndrej Zary dev_set_drvdata(pdev, card); 371f9933487SOndrej Zary err = snd_card_register(card); 372f9933487SOndrej Zary if (err < 0) 373f9933487SOndrej Zary goto error; 374f9933487SOndrej Zary #ifdef SUPPORT_JOYSTICK 375f9933487SOndrej Zary if (!gameport[ndev]) 376f9933487SOndrej Zary return 0; 377f9933487SOndrej Zary /* gameport is hardwired to 0x200 */ 378f9933487SOndrej Zary res = request_region(0x200, 8, "CMI8328 gameport"); 379f9933487SOndrej Zary if (!res) 380f9933487SOndrej Zary snd_printk(KERN_WARNING "unable to allocate gameport I/O port\n"); 381f9933487SOndrej Zary else { 382f9933487SOndrej Zary struct gameport *gp = cmi->gameport = gameport_allocate_port(); 383f9933487SOndrej Zary if (!cmi->gameport) 384f9933487SOndrej Zary release_and_free_resource(res); 385f9933487SOndrej Zary else { 386f9933487SOndrej Zary gameport_set_name(gp, "CMI8328 Gameport"); 387f9933487SOndrej Zary gameport_set_phys(gp, "%s/gameport0", dev_name(pdev)); 388f9933487SOndrej Zary gameport_set_dev_parent(gp, pdev); 389f9933487SOndrej Zary gp->io = 0x200; 390f9933487SOndrej Zary gameport_set_port_data(gp, res); 391f9933487SOndrej Zary /* Enable gameport */ 392f9933487SOndrej Zary snd_cmi8328_cfg_write(port, CFG1, 393f9933487SOndrej Zary CFG1_SB_DISABLE | CFG1_GAMEPORT); 394f9933487SOndrej Zary gameport_register_port(gp); 395f9933487SOndrej Zary } 396f9933487SOndrej Zary } 397f9933487SOndrej Zary #endif 398f9933487SOndrej Zary return 0; 399f9933487SOndrej Zary error: 400f9933487SOndrej Zary snd_card_free(card); 401f9933487SOndrej Zary 402f9933487SOndrej Zary return err; 403f9933487SOndrej Zary } 404f9933487SOndrej Zary 4051bff292eSBill Pemberton static int snd_cmi8328_remove(struct device *pdev, unsigned int dev) 406f9933487SOndrej Zary { 407f9933487SOndrej Zary struct snd_card *card = dev_get_drvdata(pdev); 408f9933487SOndrej Zary struct snd_cmi8328 *cmi = card->private_data; 40956244d08STakashi Iwai 41056244d08STakashi Iwai #ifdef SUPPORT_JOYSTICK 411f9933487SOndrej Zary if (cmi->gameport) { 412f9933487SOndrej Zary struct resource *res = gameport_get_port_data(cmi->gameport); 413f9933487SOndrej Zary gameport_unregister_port(cmi->gameport); 414f9933487SOndrej Zary release_and_free_resource(res); 415f9933487SOndrej Zary } 416f9933487SOndrej Zary #endif 417f9933487SOndrej Zary /* disable everything */ 418f9933487SOndrej Zary snd_cmi8328_cfg_write(cmi->port, CFG1, CFG1_SB_DISABLE); 419f9933487SOndrej Zary snd_cmi8328_cfg_write(cmi->port, CFG2, 0); 420f9933487SOndrej Zary snd_cmi8328_cfg_write(cmi->port, CFG3, 0); 421f9933487SOndrej Zary snd_card_free(card); 422f9933487SOndrej Zary return 0; 423f9933487SOndrej Zary } 424f9933487SOndrej Zary 425f9933487SOndrej Zary #ifdef CONFIG_PM 426f9933487SOndrej Zary static int snd_cmi8328_suspend(struct device *pdev, unsigned int n, 427f9933487SOndrej Zary pm_message_t state) 428f9933487SOndrej Zary { 429f9933487SOndrej Zary struct snd_card *card = dev_get_drvdata(pdev); 430f9933487SOndrej Zary struct snd_cmi8328 *cmi; 431f9933487SOndrej Zary 432f9933487SOndrej Zary if (!card) /* ignore absent devices */ 433f9933487SOndrej Zary return 0; 434f9933487SOndrej Zary cmi = card->private_data; 435f9933487SOndrej Zary snd_cmi8328_cfg_save(cmi->port, cmi->cfg); 436f9933487SOndrej Zary snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 437f9933487SOndrej Zary snd_pcm_suspend_all(cmi->wss->pcm); 438f9933487SOndrej Zary cmi->wss->suspend(cmi->wss); 439f9933487SOndrej Zary 440f9933487SOndrej Zary return 0; 441f9933487SOndrej Zary } 442f9933487SOndrej Zary 443f9933487SOndrej Zary static int snd_cmi8328_resume(struct device *pdev, unsigned int n) 444f9933487SOndrej Zary { 445f9933487SOndrej Zary struct snd_card *card = dev_get_drvdata(pdev); 446f9933487SOndrej Zary struct snd_cmi8328 *cmi; 447f9933487SOndrej Zary 448f9933487SOndrej Zary if (!card) /* ignore absent devices */ 449f9933487SOndrej Zary return 0; 450f9933487SOndrej Zary cmi = card->private_data; 451f9933487SOndrej Zary snd_cmi8328_cfg_restore(cmi->port, cmi->cfg); 452f9933487SOndrej Zary outb(cmi->wss_cfg, cmi->port); 453f9933487SOndrej Zary cmi->wss->resume(cmi->wss); 454f9933487SOndrej Zary snd_power_change_state(card, SNDRV_CTL_POWER_D0); 455f9933487SOndrej Zary 456f9933487SOndrej Zary return 0; 457f9933487SOndrej Zary } 458f9933487SOndrej Zary #endif 459f9933487SOndrej Zary 460f9933487SOndrej Zary static struct isa_driver snd_cmi8328_driver = { 461f9933487SOndrej Zary .probe = snd_cmi8328_probe, 4621bff292eSBill Pemberton .remove = snd_cmi8328_remove, 463f9933487SOndrej Zary #ifdef CONFIG_PM 464f9933487SOndrej Zary .suspend = snd_cmi8328_suspend, 465f9933487SOndrej Zary .resume = snd_cmi8328_resume, 466f9933487SOndrej Zary #endif 467f9933487SOndrej Zary .driver = { 468f9933487SOndrej Zary .name = "cmi8328" 469f9933487SOndrej Zary }, 470f9933487SOndrej Zary }; 471f9933487SOndrej Zary 472f9933487SOndrej Zary static int __init alsa_card_cmi8328_init(void) 473f9933487SOndrej Zary { 474f9933487SOndrej Zary return isa_register_driver(&snd_cmi8328_driver, CMI8328_MAX); 475f9933487SOndrej Zary } 476f9933487SOndrej Zary 477f9933487SOndrej Zary static void __exit alsa_card_cmi8328_exit(void) 478f9933487SOndrej Zary { 479f9933487SOndrej Zary isa_unregister_driver(&snd_cmi8328_driver); 480f9933487SOndrej Zary } 481f9933487SOndrej Zary 482f9933487SOndrej Zary module_init(alsa_card_cmi8328_init) 483f9933487SOndrej Zary module_exit(alsa_card_cmi8328_exit) 484