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