📄 cs4281.c
字号:
spin_lock_irq(&chip->reg_lock); chip->midcr |= BA0_MIDCR_RXE; chip->midi_input = substream; if (!(chip->uartm & CS4281_MODE_OUTPUT)) { snd_cs4281_midi_reset(chip); } else { snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); } spin_unlock_irq(&chip->reg_lock); return 0;}static int snd_cs4281_midi_input_close(struct snd_rawmidi_substream *substream){ struct cs4281 *chip = substream->rmidi->private_data; spin_lock_irq(&chip->reg_lock); chip->midcr &= ~(BA0_MIDCR_RXE | BA0_MIDCR_RIE); chip->midi_input = NULL; if (!(chip->uartm & CS4281_MODE_OUTPUT)) { snd_cs4281_midi_reset(chip); } else { snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); } chip->uartm &= ~CS4281_MODE_INPUT; spin_unlock_irq(&chip->reg_lock); return 0;}static int snd_cs4281_midi_output_open(struct snd_rawmidi_substream *substream){ struct cs4281 *chip = substream->rmidi->private_data; spin_lock_irq(&chip->reg_lock); chip->uartm |= CS4281_MODE_OUTPUT; chip->midcr |= BA0_MIDCR_TXE; chip->midi_output = substream; if (!(chip->uartm & CS4281_MODE_INPUT)) { snd_cs4281_midi_reset(chip); } else { snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); } spin_unlock_irq(&chip->reg_lock); return 0;}static int snd_cs4281_midi_output_close(struct snd_rawmidi_substream *substream){ struct cs4281 *chip = substream->rmidi->private_data; spin_lock_irq(&chip->reg_lock); chip->midcr &= ~(BA0_MIDCR_TXE | BA0_MIDCR_TIE); chip->midi_output = NULL; if (!(chip->uartm & CS4281_MODE_INPUT)) { snd_cs4281_midi_reset(chip); } else { snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); } chip->uartm &= ~CS4281_MODE_OUTPUT; spin_unlock_irq(&chip->reg_lock); return 0;}static void snd_cs4281_midi_input_trigger(struct snd_rawmidi_substream *substream, int up){ unsigned long flags; struct cs4281 *chip = substream->rmidi->private_data; spin_lock_irqsave(&chip->reg_lock, flags); if (up) { if ((chip->midcr & BA0_MIDCR_RIE) == 0) { chip->midcr |= BA0_MIDCR_RIE; snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); } } else { if (chip->midcr & BA0_MIDCR_RIE) { chip->midcr &= ~BA0_MIDCR_RIE; snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); } } spin_unlock_irqrestore(&chip->reg_lock, flags);}static void snd_cs4281_midi_output_trigger(struct snd_rawmidi_substream *substream, int up){ unsigned long flags; struct cs4281 *chip = substream->rmidi->private_data; unsigned char byte; spin_lock_irqsave(&chip->reg_lock, flags); if (up) { if ((chip->midcr & BA0_MIDCR_TIE) == 0) { chip->midcr |= BA0_MIDCR_TIE; /* fill UART FIFO buffer at first, and turn Tx interrupts only if necessary */ while ((chip->midcr & BA0_MIDCR_TIE) && (snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_TBF) == 0) { if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { chip->midcr &= ~BA0_MIDCR_TIE; } else { snd_cs4281_pokeBA0(chip, BA0_MIDWP, byte); } } snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); } } else { if (chip->midcr & BA0_MIDCR_TIE) { chip->midcr &= ~BA0_MIDCR_TIE; snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); } } spin_unlock_irqrestore(&chip->reg_lock, flags);}static struct snd_rawmidi_ops snd_cs4281_midi_output ={ .open = snd_cs4281_midi_output_open, .close = snd_cs4281_midi_output_close, .trigger = snd_cs4281_midi_output_trigger,};static struct snd_rawmidi_ops snd_cs4281_midi_input ={ .open = snd_cs4281_midi_input_open, .close = snd_cs4281_midi_input_close, .trigger = snd_cs4281_midi_input_trigger,};static int __devinit snd_cs4281_midi(struct cs4281 * chip, int device, struct snd_rawmidi **rrawmidi){ struct snd_rawmidi *rmidi; int err; if (rrawmidi) *rrawmidi = NULL; if ((err = snd_rawmidi_new(chip->card, "CS4281", device, 1, 1, &rmidi)) < 0) return err; strcpy(rmidi->name, "CS4281"); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs4281_midi_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs4281_midi_input); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; rmidi->private_data = chip; chip->rmidi = rmidi; if (rrawmidi) *rrawmidi = rmidi; return 0;}/* * Interrupt handler */static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id){ struct cs4281 *chip = dev_id; unsigned int status, dma, val; struct cs4281_dma *cdma; if (chip == NULL) return IRQ_NONE; status = snd_cs4281_peekBA0(chip, BA0_HISR); if ((status & 0x7fffffff) == 0) { snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_EOI); return IRQ_NONE; } if (status & (BA0_HISR_DMA(0)|BA0_HISR_DMA(1)|BA0_HISR_DMA(2)|BA0_HISR_DMA(3))) { for (dma = 0; dma < 4; dma++) if (status & BA0_HISR_DMA(dma)) { cdma = &chip->dma[dma]; spin_lock(&chip->reg_lock); /* ack DMA IRQ */ val = snd_cs4281_peekBA0(chip, cdma->regHDSR); /* workaround, sometimes CS4281 acknowledges */ /* end or middle transfer position twice */ cdma->frag++; if ((val & BA0_HDSR_DHTC) && !(cdma->frag & 1)) { cdma->frag--; chip->spurious_dhtc_irq++; spin_unlock(&chip->reg_lock); continue; } if ((val & BA0_HDSR_DTC) && (cdma->frag & 1)) { cdma->frag--; chip->spurious_dtc_irq++; spin_unlock(&chip->reg_lock); continue; } spin_unlock(&chip->reg_lock); snd_pcm_period_elapsed(cdma->substream); } } if ((status & BA0_HISR_MIDI) && chip->rmidi) { unsigned char c; spin_lock(&chip->reg_lock); while ((snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_RBE) == 0) { c = snd_cs4281_peekBA0(chip, BA0_MIDRP); if ((chip->midcr & BA0_MIDCR_RIE) == 0) continue; snd_rawmidi_receive(chip->midi_input, &c, 1); } while ((snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_TBF) == 0) { if ((chip->midcr & BA0_MIDCR_TIE) == 0) break; if (snd_rawmidi_transmit(chip->midi_output, &c, 1) != 1) { chip->midcr &= ~BA0_MIDCR_TIE; snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); break; } snd_cs4281_pokeBA0(chip, BA0_MIDWP, c); } spin_unlock(&chip->reg_lock); } /* EOI to the PCI part... reenables interrupts */ snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_EOI); return IRQ_HANDLED;}/* * OPL3 command */static void snd_cs4281_opl3_command(struct snd_opl3 *opl3, unsigned short cmd, unsigned char val){ unsigned long flags; struct cs4281 *chip = opl3->private_data; void __iomem *port; if (cmd & OPL3_RIGHT) port = chip->ba0 + BA0_B1AP; /* right port */ else port = chip->ba0 + BA0_B0AP; /* left port */ spin_lock_irqsave(&opl3->reg_lock, flags); writel((unsigned int)cmd, port); udelay(10); writel((unsigned int)val, port + 4); udelay(30); spin_unlock_irqrestore(&opl3->reg_lock, flags);}static int __devinit snd_cs4281_probe(struct pci_dev *pci, const struct pci_device_id *pci_id){ static int dev; struct snd_card *card; struct cs4281 *chip; struct snd_opl3 *opl3; int err; if (dev >= SNDRV_CARDS) return -ENODEV; if (!enable[dev]) { dev++; return -ENOENT; } card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); if (card == NULL) return -ENOMEM; if ((err = snd_cs4281_create(card, pci, &chip, dual_codec[dev])) < 0) { snd_card_free(card); return err; } card->private_data = chip; if ((err = snd_cs4281_mixer(chip)) < 0) { snd_card_free(card); return err; } if ((err = snd_cs4281_pcm(chip, 0, NULL)) < 0) { snd_card_free(card); return err; } if ((err = snd_cs4281_midi(chip, 0, NULL)) < 0) { snd_card_free(card); return err; } if ((err = snd_opl3_new(card, OPL3_HW_OPL3_CS4281, &opl3)) < 0) { snd_card_free(card); return err; } opl3->private_data = chip; opl3->command = snd_cs4281_opl3_command; snd_opl3_init(opl3); if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { snd_card_free(card); return err; } snd_cs4281_create_gameport(chip); strcpy(card->driver, "CS4281"); strcpy(card->shortname, "Cirrus Logic CS4281"); sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname, chip->ba0_addr, chip->irq); if ((err = snd_card_register(card)) < 0) { snd_card_free(card); return err; } pci_set_drvdata(pci, card); dev++; return 0;}static void __devexit snd_cs4281_remove(struct pci_dev *pci){ snd_card_free(pci_get_drvdata(pci)); pci_set_drvdata(pci, NULL);}/* * Power Management */#ifdef CONFIG_PMstatic int saved_regs[SUSPEND_REGISTERS] = { BA0_JSCTL, BA0_GPIOR, BA0_SSCR, BA0_MIDCR, BA0_SRCSA, BA0_PASR, BA0_CASR, BA0_DACSR, BA0_ADCSR, BA0_FMLVC, BA0_FMRVC, BA0_PPLVC, BA0_PPRVC,};#define CLKCR1_CKRA 0x00010000Lstatic int cs4281_suspend(struct pci_dev *pci, pm_message_t state){ struct snd_card *card = pci_get_drvdata(pci); struct cs4281 *chip = card->private_data; u32 ulCLK; unsigned int i; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); snd_pcm_suspend_all(chip->pcm); snd_ac97_suspend(chip->ac97); snd_ac97_suspend(chip->ac97_secondary); ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1); ulCLK |= CLKCR1_CKRA; snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK); /* Disable interrupts. */ snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_CHGM); /* remember the status registers */ for (i = 0; i < ARRAY_SIZE(saved_regs); i++) if (saved_regs[i]) chip->suspend_regs[i] = snd_cs4281_peekBA0(chip, saved_regs[i]); /* Turn off the serial ports. */ snd_cs4281_pokeBA0(chip, BA0_SERMC, 0); /* Power off FM, Joystick, AC link, */ snd_cs4281_pokeBA0(chip, BA0_SSPM, 0); /* DLL off. */ snd_cs4281_pokeBA0(chip, BA0_CLKCR1, 0); /* AC link off. */ snd_cs4281_pokeBA0(chip, BA0_SPMC, 0); ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1); ulCLK &= ~CLKCR1_CKRA; snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK); pci_disable_device(pci); pci_save_state(pci); pci_set_power_state(pci, pci_choose_state(pci, state)); return 0;}static int cs4281_resume(struct pci_dev *pci){ struct snd_card *card = pci_get_drvdata(pci); struct cs4281 *chip = card->private_data; unsigned int i; u32 ulCLK; pci_set_power_state(pci, PCI_D0); pci_restore_state(pci); if (pci_enable_device(pci) < 0) { printk(KERN_ERR "cs4281: pci_enable_device failed, " "disabling device\n"); snd_card_disconnect(card); return -EIO; } pci_set_master(pci); ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1); ulCLK |= CLKCR1_CKRA; snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK); snd_cs4281_chip_init(
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -