📄 nm256.c
字号:
static intsnd_nm256_capture_close(struct snd_pcm_substream *substream){ struct nm256 *chip = snd_pcm_substream_chip(substream); snd_nm256_release_irq(chip); return 0;}/* * create a pcm instance */static struct snd_pcm_ops snd_nm256_playback_ops = { .open = snd_nm256_playback_open, .close = snd_nm256_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_nm256_pcm_hw_params, .prepare = snd_nm256_pcm_prepare, .trigger = snd_nm256_playback_trigger, .pointer = snd_nm256_playback_pointer,#ifndef __i386__ .copy = snd_nm256_playback_copy, .silence = snd_nm256_playback_silence,#endif .mmap = snd_pcm_lib_mmap_iomem,};static struct snd_pcm_ops snd_nm256_capture_ops = { .open = snd_nm256_capture_open, .close = snd_nm256_capture_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_nm256_pcm_hw_params, .prepare = snd_nm256_pcm_prepare, .trigger = snd_nm256_capture_trigger, .pointer = snd_nm256_capture_pointer,#ifndef __i386__ .copy = snd_nm256_capture_copy,#endif .mmap = snd_pcm_lib_mmap_iomem,};static int __devinitsnd_nm256_pcm(struct nm256 *chip, int device){ struct snd_pcm *pcm; int i, err; for (i = 0; i < 2; i++) { struct nm256_stream *s = &chip->streams[i]; s->bufptr = chip->buffer + (s->buf - chip->buffer_start); s->bufptr_addr = chip->buffer_addr + (s->buf - chip->buffer_start); } err = snd_pcm_new(chip->card, chip->card->driver, device, 1, 1, &pcm); if (err < 0) return err; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_nm256_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_nm256_capture_ops); pcm->private_data = chip; pcm->info_flags = 0; chip->pcm = pcm; return 0;}/* * Initialize the hardware. */static voidsnd_nm256_init_chip(struct nm256 *chip){ /* Reset everything. */ snd_nm256_writeb(chip, 0x0, 0x11); snd_nm256_writew(chip, 0x214, 0); /* stop sounds.. */ //snd_nm256_playback_stop(chip); //snd_nm256_capture_stop(chip);}static irqreturn_tsnd_nm256_intr_check(struct nm256 *chip){ if (chip->badintrcount++ > 1000) { /* * I'm not sure if the best thing is to stop the card from * playing or just release the interrupt (after all, we're in * a bad situation, so doing fancy stuff may not be such a good * idea). * * I worry about the card engine continuing to play noise * over and over, however--that could become a very * obnoxious problem. And we know that when this usually * happens things are fairly safe, it just means the user's * inserted a PCMCIA card and someone's spamming us with IRQ 9s. */ if (chip->streams[SNDRV_PCM_STREAM_PLAYBACK].running) snd_nm256_playback_stop(chip); if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running) snd_nm256_capture_stop(chip); chip->badintrcount = 0; return IRQ_HANDLED; } return IRQ_NONE;}/* * Handle a potential interrupt for the device referred to by DEV_ID. * * I don't like the cut-n-paste job here either between the two routines, * but there are sufficient differences between the two interrupt handlers * that parameterizing it isn't all that great either. (Could use a macro, * I suppose...yucky bleah.) */static irqreturn_tsnd_nm256_interrupt(int irq, void *dev_id){ struct nm256 *chip = dev_id; u16 status; u8 cbyte; status = snd_nm256_readw(chip, NM_INT_REG); /* Not ours. */ if (status == 0) return snd_nm256_intr_check(chip); chip->badintrcount = 0; /* Rather boring; check for individual interrupts and process them. */ spin_lock(&chip->reg_lock); if (status & NM_PLAYBACK_INT) { status &= ~NM_PLAYBACK_INT; NM_ACK_INT(chip, NM_PLAYBACK_INT); snd_nm256_playback_update(chip); } if (status & NM_RECORD_INT) { status &= ~NM_RECORD_INT; NM_ACK_INT(chip, NM_RECORD_INT); snd_nm256_capture_update(chip); } if (status & NM_MISC_INT_1) { status &= ~NM_MISC_INT_1; NM_ACK_INT(chip, NM_MISC_INT_1); snd_printd("NM256: Got misc interrupt #1\n"); snd_nm256_writew(chip, NM_INT_REG, 0x8000); cbyte = snd_nm256_readb(chip, 0x400); snd_nm256_writeb(chip, 0x400, cbyte | 2); } if (status & NM_MISC_INT_2) { status &= ~NM_MISC_INT_2; NM_ACK_INT(chip, NM_MISC_INT_2); snd_printd("NM256: Got misc interrupt #2\n"); cbyte = snd_nm256_readb(chip, 0x400); snd_nm256_writeb(chip, 0x400, cbyte & ~2); } /* Unknown interrupt. */ if (status) { snd_printd("NM256: Fire in the hole! Unknown status 0x%x\n", status); /* Pray. */ NM_ACK_INT(chip, status); } spin_unlock(&chip->reg_lock); return IRQ_HANDLED;}/* * Handle a potential interrupt for the device referred to by DEV_ID. * This handler is for the 256ZX, and is very similar to the non-ZX * routine. */static irqreturn_tsnd_nm256_interrupt_zx(int irq, void *dev_id){ struct nm256 *chip = dev_id; u32 status; u8 cbyte; status = snd_nm256_readl(chip, NM_INT_REG); /* Not ours. */ if (status == 0) return snd_nm256_intr_check(chip); chip->badintrcount = 0; /* Rather boring; check for individual interrupts and process them. */ spin_lock(&chip->reg_lock); if (status & NM2_PLAYBACK_INT) { status &= ~NM2_PLAYBACK_INT; NM2_ACK_INT(chip, NM2_PLAYBACK_INT); snd_nm256_playback_update(chip); } if (status & NM2_RECORD_INT) { status &= ~NM2_RECORD_INT; NM2_ACK_INT(chip, NM2_RECORD_INT); snd_nm256_capture_update(chip); } if (status & NM2_MISC_INT_1) { status &= ~NM2_MISC_INT_1; NM2_ACK_INT(chip, NM2_MISC_INT_1); snd_printd("NM256: Got misc interrupt #1\n"); cbyte = snd_nm256_readb(chip, 0x400); snd_nm256_writeb(chip, 0x400, cbyte | 2); } if (status & NM2_MISC_INT_2) { status &= ~NM2_MISC_INT_2; NM2_ACK_INT(chip, NM2_MISC_INT_2); snd_printd("NM256: Got misc interrupt #2\n"); cbyte = snd_nm256_readb(chip, 0x400); snd_nm256_writeb(chip, 0x400, cbyte & ~2); } /* Unknown interrupt. */ if (status) { snd_printd("NM256: Fire in the hole! Unknown status 0x%x\n", status); /* Pray. */ NM2_ACK_INT(chip, status); } spin_unlock(&chip->reg_lock); return IRQ_HANDLED;}/* * AC97 interface *//* * Waits for the mixer to become ready to be written; returns a zero value * if it timed out. */static intsnd_nm256_ac97_ready(struct nm256 *chip){ int timeout = 10; u32 testaddr; u16 testb; testaddr = chip->mixer_status_offset; testb = chip->mixer_status_mask; /* * Loop around waiting for the mixer to become ready. */ while (timeout-- > 0) { if ((snd_nm256_readw(chip, testaddr) & testb) == 0) return 1; udelay(100); } return 0;}/* * Initial register values to be written to the AC97 mixer. * While most of these are identical to the reset values, we do this * so that we have most of the register contents cached--this avoids * reading from the mixer directly (which seems to be problematic, * probably due to ignorance). */struct initialValues { unsigned short reg; unsigned short value;};static struct initialValues nm256_ac97_init_val[] ={ { AC97_MASTER, 0x8000 }, { AC97_HEADPHONE, 0x8000 }, { AC97_MASTER_MONO, 0x8000 }, { AC97_PC_BEEP, 0x8000 }, { AC97_PHONE, 0x8008 }, { AC97_MIC, 0x8000 }, { AC97_LINE, 0x8808 }, { AC97_CD, 0x8808 }, { AC97_VIDEO, 0x8808 }, { AC97_AUX, 0x8808 }, { AC97_PCM, 0x8808 }, { AC97_REC_SEL, 0x0000 }, { AC97_REC_GAIN, 0x0B0B }, { AC97_GENERAL_PURPOSE, 0x0000 }, { AC97_3D_CONTROL, 0x8000 }, { AC97_VENDOR_ID1, 0x8384 }, { AC97_VENDOR_ID2, 0x7609 },};static int nm256_ac97_idx(unsigned short reg){ int i; for (i = 0; i < ARRAY_SIZE(nm256_ac97_init_val); i++) if (nm256_ac97_init_val[i].reg == reg) return i; return -1;}/* * some nm256 easily crash when reading from mixer registers * thus we're treating it as a write-only mixer and cache the * written values */static unsigned shortsnd_nm256_ac97_read(struct snd_ac97 *ac97, unsigned short reg){ struct nm256 *chip = ac97->private_data; int idx = nm256_ac97_idx(reg); if (idx < 0) return 0; return chip->ac97_regs[idx];}/* */static voidsnd_nm256_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val){ struct nm256 *chip = ac97->private_data; int tries = 2; int idx = nm256_ac97_idx(reg); u32 base; if (idx < 0) return; base = chip->mixer_base; snd_nm256_ac97_ready(chip); /* Wait for the write to take, too. */ while (tries-- > 0) { snd_nm256_writew(chip, base + reg, val); msleep(1); /* a little delay here seems better.. */ if (snd_nm256_ac97_ready(chip)) { /* successful write: set cache */ chip->ac97_regs[idx] = val; return; } } snd_printd("nm256: ac97 codec not ready..\n");}/* static resolution table */static struct snd_ac97_res_table nm256_res_table[] = { { AC97_MASTER, 0x1f1f }, { AC97_HEADPHONE, 0x1f1f }, { AC97_MASTER_MONO, 0x001f }, { AC97_PC_BEEP, 0x001f }, { AC97_PHONE, 0x001f }, { AC97_MIC, 0x001f }, { AC97_LINE, 0x1f1f }, { AC97_CD, 0x1f1f }, { AC97_VIDEO, 0x1f1f }, { AC97_AUX, 0x1f1f }, { AC97_PCM, 0x1f1f }, { AC97_REC_GAIN, 0x0f0f }, { } /* terminator */};/* initialize the ac97 into a known state */static voidsnd_nm256_ac97_reset(struct snd_ac97 *ac97){ struct nm256 *chip = ac97->private_data; /* Reset the mixer. 'Tis magic! */ snd_nm256_writeb(chip, 0x6c0, 1); if (! chip->reset_workaround) { /* Dell latitude LS will lock up by this */ snd_nm256_writeb(chip, 0x6cc, 0x87); } if (! chip->reset_workaround_2) { /* Dell latitude CSx will lock up by this */ snd_nm256_writeb(chip, 0x6cc, 0x80); snd_nm256_writeb(chip, 0x6cc, 0x0); } if (! chip->in_resume) { int i; for (i = 0; i < ARRAY_SIZE(nm256_ac97_init_val); i++) { /* preload the cache, so as to avoid even a single * read of the mixer regs */ snd_nm256_ac97_write(ac97, nm256_ac97_init_val[i].reg, nm256_ac97_init_val[i].value); } }}/* create an ac97 mixer interface */static int __devinitsnd_nm256_mixer(struct nm256 *chip){ struct snd_ac97_bus *pbus; struct snd_ac97_template ac97; int err; static struct snd_ac97_bus_ops ops = { .reset = snd_nm256_ac97_reset, .write = snd_nm256_ac97_write, .read = snd_nm256_ac97_read, }; chip->ac97_regs = kcalloc(sizeof(short), ARRAY_SIZE(nm256_ac97_init_val), GFP_KERNEL); if (! chip->ac97_regs) return -ENOMEM; if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0) return err; memset(&ac97, 0, sizeof(ac97)); ac97.scaps = AC97_SCAP_AUDIO; /* we support audio! */ ac97.private_data = chip; ac97.res_table = nm256_res_table; pbus->no_vra = 1; err = snd_ac97_mixer(pbus, &ac97, &chip->ac97); if (err < 0) return err; if (! (chip->ac97->id & (0xf0000000))) { /* looks like an invalid id */ sprintf(chip->card->mixername, "%s AC97", chip->card->driver); } return 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -