📄 es1968.c
字号:
*/static void es1968_update_hw_volume(unsigned long private_data){ struct es1968 *chip = (struct es1968 *) private_data; int x, val; unsigned long flags; /* Figure out which volume control button was pushed, based on differences from the default register values. */ x = inb(chip->io_port + 0x1c) & 0xee; /* Reset the volume control registers. */ outb(0x88, chip->io_port + 0x1c); outb(0x88, chip->io_port + 0x1d); outb(0x88, chip->io_port + 0x1e); outb(0x88, chip->io_port + 0x1f); if (chip->in_suspend) return; if (! chip->master_switch || ! chip->master_volume) return; /* FIXME: we can't call snd_ac97_* functions since here is in tasklet. */ spin_lock_irqsave(&chip->ac97_lock, flags); val = chip->ac97->regs[AC97_MASTER]; switch (x) { case 0x88: /* mute */ val ^= 0x8000; chip->ac97->regs[AC97_MASTER] = val; outw(val, chip->io_port + ESM_AC97_DATA); outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX); snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id); break; case 0xaa: /* volume up */ if ((val & 0x7f) > 0) val--; if ((val & 0x7f00) > 0) val -= 0x0100; chip->ac97->regs[AC97_MASTER] = val; outw(val, chip->io_port + ESM_AC97_DATA); outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX); snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id); break; case 0x66: /* volume down */ if ((val & 0x7f) < 0x1f) val++; if ((val & 0x7f00) < 0x1f00) val += 0x0100; chip->ac97->regs[AC97_MASTER] = val; outw(val, chip->io_port + ESM_AC97_DATA); outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX); snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id); break; } spin_unlock_irqrestore(&chip->ac97_lock, flags);}/* * interrupt handler */static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id){ struct es1968 *chip = dev_id; u32 event; if (!(event = inb(chip->io_port + 0x1A))) return IRQ_NONE; outw(inw(chip->io_port + 4) & 1, chip->io_port + 4); if (event & ESM_HWVOL_IRQ) tasklet_hi_schedule(&chip->hwvol_tq); /* we'll do this later */ /* else ack 'em all, i imagine */ outb(0xFF, chip->io_port + 0x1A); if ((event & ESM_MPU401_IRQ) && chip->rmidi) { snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data); } if (event & ESM_SOUND_IRQ) { struct esschan *es; spin_lock(&chip->substream_lock); list_for_each_entry(es, &chip->substream_list, list) { if (es->running) snd_es1968_update_pcm(chip, es); } spin_unlock(&chip->substream_lock); if (chip->in_measurement) { unsigned int curp = __apu_get_register(chip, chip->measure_apu, 5); if (curp < chip->measure_lastpos) chip->measure_count++; chip->measure_lastpos = curp; } } return IRQ_HANDLED;}/* * Mixer stuff */static int __devinitsnd_es1968_mixer(struct es1968 *chip){ struct snd_ac97_bus *pbus; struct snd_ac97_template ac97; struct snd_ctl_elem_id id; int err; static struct snd_ac97_bus_ops ops = { .write = snd_es1968_ac97_write, .read = snd_es1968_ac97_read, }; if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0) return err; pbus->no_vra = 1; /* ES1968 doesn't need VRA */ memset(&ac97, 0, sizeof(ac97)); ac97.private_data = chip; if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97)) < 0) return err; /* attach master switch / volumes for h/w volume control */ memset(&id, 0, sizeof(id)); id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; strcpy(id.name, "Master Playback Switch"); chip->master_switch = snd_ctl_find_id(chip->card, &id); memset(&id, 0, sizeof(id)); id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; strcpy(id.name, "Master Playback Volume"); chip->master_volume = snd_ctl_find_id(chip->card, &id); return 0;}/* * reset ac97 codec */static void snd_es1968_ac97_reset(struct es1968 *chip){ unsigned long ioaddr = chip->io_port; unsigned short save_ringbus_a; unsigned short save_68; unsigned short w; unsigned int vend; /* save configuration */ save_ringbus_a = inw(ioaddr + 0x36); //outw(inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); /* clear second codec id? */ /* set command/status address i/o to 1st codec */ outw(inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); outw(inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); /* disable ac link */ outw(0x0000, ioaddr + 0x36); save_68 = inw(ioaddr + 0x68); pci_read_config_word(chip->pci, 0x58, &w); /* something magical with gpio and bus arb. */ pci_read_config_dword(chip->pci, PCI_SUBSYSTEM_VENDOR_ID, &vend); if (w & 1) save_68 |= 0x10; outw(0xfffe, ioaddr + 0x64); /* unmask gpio 0 */ outw(0x0001, ioaddr + 0x68); /* gpio write */ outw(0x0000, ioaddr + 0x60); /* write 0 to gpio 0 */ udelay(20); outw(0x0001, ioaddr + 0x60); /* write 1 to gpio 1 */ msleep(20); outw(save_68 | 0x1, ioaddr + 0x68); /* now restore .. */ outw((inw(ioaddr + 0x38) & 0xfffc) | 0x1, ioaddr + 0x38); outw((inw(ioaddr + 0x3a) & 0xfffc) | 0x1, ioaddr + 0x3a); outw((inw(ioaddr + 0x3c) & 0xfffc) | 0x1, ioaddr + 0x3c); /* now the second codec */ /* disable ac link */ outw(0x0000, ioaddr + 0x36); outw(0xfff7, ioaddr + 0x64); /* unmask gpio 3 */ save_68 = inw(ioaddr + 0x68); outw(0x0009, ioaddr + 0x68); /* gpio write 0 & 3 ?? */ outw(0x0001, ioaddr + 0x60); /* write 1 to gpio */ udelay(20); outw(0x0009, ioaddr + 0x60); /* write 9 to gpio */ msleep(500); //outw(inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); outw(inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); outw(inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c);#if 0 /* the loop here needs to be much better if we want it.. */ snd_printk(KERN_INFO "trying software reset\n"); /* try and do a software reset */ outb(0x80 | 0x7c, ioaddr + 0x30); for (w = 0;; w++) { if ((inw(ioaddr + 0x30) & 1) == 0) { if (inb(ioaddr + 0x32) != 0) break; outb(0x80 | 0x7d, ioaddr + 0x30); if (((inw(ioaddr + 0x30) & 1) == 0) && (inb(ioaddr + 0x32) != 0)) break; outb(0x80 | 0x7f, ioaddr + 0x30); if (((inw(ioaddr + 0x30) & 1) == 0) && (inb(ioaddr + 0x32) != 0)) break; } if (w > 10000) { outb(inb(ioaddr + 0x37) | 0x08, ioaddr + 0x37); /* do a software reset */ msleep(500); /* oh my.. */ outb(inb(ioaddr + 0x37) & ~0x08, ioaddr + 0x37); udelay(1); outw(0x80, ioaddr + 0x30); for (w = 0; w < 10000; w++) { if ((inw(ioaddr + 0x30) & 1) == 0) break; } } }#endif if (vend == NEC_VERSA_SUBID1 || vend == NEC_VERSA_SUBID2) { /* turn on external amp? */ outw(0xf9ff, ioaddr + 0x64); outw(inw(ioaddr + 0x68) | 0x600, ioaddr + 0x68); outw(0x0209, ioaddr + 0x60); } /* restore.. */ outw(save_ringbus_a, ioaddr + 0x36); /* Turn on the 978 docking chip. First frob the "master output enable" bit, then set most of the playback volume control registers to max. */ outb(inb(ioaddr+0xc0)|(1<<5), ioaddr+0xc0); outb(0xff, ioaddr+0xc3); outb(0xff, ioaddr+0xc4); outb(0xff, ioaddr+0xc6); outb(0xff, ioaddr+0xc8); outb(0x3f, ioaddr+0xcf); outb(0x3f, ioaddr+0xd0);}static void snd_es1968_reset(struct es1968 *chip){ /* Reset */ outw(ESM_RESET_MAESTRO | ESM_RESET_DIRECTSOUND, chip->io_port + ESM_PORT_HOST_IRQ); udelay(10); outw(0x0000, chip->io_port + ESM_PORT_HOST_IRQ); udelay(10);}/* * initialize maestro chip */static void snd_es1968_chip_init(struct es1968 *chip){ struct pci_dev *pci = chip->pci; int i; unsigned long iobase = chip->io_port; u16 w; u32 n; /* We used to muck around with pci config space that * we had no business messing with. We don't know enough * about the machine to know which DMA mode is appropriate, * etc. We were guessing wrong on some machines and making * them unhappy. We now trust in the BIOS to do things right, * which almost certainly means a new host of problems will * arise with broken BIOS implementations. screw 'em. * We're already intolerant of machines that don't assign * IRQs. */ /* Config Reg A */ pci_read_config_word(pci, ESM_CONFIG_A, &w); w &= ~DMA_CLEAR; /* Clear DMA bits */ w &= ~(PIC_SNOOP1 | PIC_SNOOP2); /* Clear Pic Snoop Mode Bits */ w &= ~SAFEGUARD; /* Safeguard off */ w |= POST_WRITE; /* Posted write */ w |= PCI_TIMING; /* PCI timing on */ /* XXX huh? claims to be reserved.. */ w &= ~SWAP_LR; /* swap left/right seems to only have effect on SB Emulation */ w &= ~SUBTR_DECODE; /* Subtractive decode off */ pci_write_config_word(pci, ESM_CONFIG_A, w); /* Config Reg B */ pci_read_config_word(pci, ESM_CONFIG_B, &w); w &= ~(1 << 15); /* Turn off internal clock multiplier */ /* XXX how do we know which to use? */ w &= ~(1 << 14); /* External clock */ w &= ~SPDIF_CONFB; /* disable S/PDIF output */ w |= HWV_CONFB; /* HWV on */ w |= DEBOUNCE; /* Debounce off: easier to push the HW buttons */ w &= ~GPIO_CONFB; /* GPIO 4:5 */ w |= CHI_CONFB; /* Disconnect from the CHI. Enabling this made a dell 7500 work. */ w &= ~IDMA_CONFB; /* IDMA off (undocumented) */ w &= ~MIDI_FIX; /* MIDI fix off (undoc) */ w &= ~(1 << 1); /* reserved, always write 0 */ w &= ~IRQ_TO_ISA; /* IRQ to ISA off (undoc) */ pci_write_config_word(pci, ESM_CONFIG_B, w); /* DDMA off */ pci_read_config_word(pci, ESM_DDMA, &w); w &= ~(1 << 0); pci_write_config_word(pci, ESM_DDMA, w); /* * Legacy mode */ pci_read_config_word(pci, ESM_LEGACY_AUDIO_CONTROL, &w); w |= ESS_DISABLE_AUDIO; /* Disable Legacy Audio */ w &= ~ESS_ENABLE_SERIAL_IRQ; /* Disable SIRQ */ w &= ~(0x1f); /* disable mpu irq/io, game port, fm, SB */ pci_write_config_word(pci, ESM_LEGACY_AUDIO_CONTROL, w); /* Set up 978 docking control chip. */ pci_read_config_word(pci, 0x58, &w); w|=1<<2; /* Enable 978. */ w|=1<<3; /* Turn on 978 hardware volume control. */ w&=~(1<<11); /* Turn on 978 mixer volume control. */ pci_write_config_word(pci, 0x58, w); /* Sound Reset */ snd_es1968_reset(chip); /* * Ring Bus Setup */ /* setup usual 0x34 stuff.. 0x36 may be chip specific */ outw(0xC090, iobase + ESM_RING_BUS_DEST); /* direct sound, stereo */ udelay(20); outw(0x3000, iobase + ESM_RING_BUS_CONTR_A); /* enable ringbus/serial */ udelay(20); /* * Reset the CODEC */ snd_es1968_ac97_reset(chip); /* Ring Bus Control B */ n = inl(iobase + ESM_RING_BUS_CONTR_B); n &= ~RINGB_EN_SPDIF; /* SPDIF off */ //w |= RINGB_EN_2CODEC; /* enable 2nd codec */ outl(n, iobase + ESM_RING_BUS_CONTR_B); /* Set hardware volume control registers to midpoints. We can tell which button was pushed based on how they change. */ outb(0x88, iobase+0x1c); outb(0x88, iobase+0x1d); outb(0x88, iobase+0x1e); outb(0x88, iobase+0x1f); /* it appears some maestros (dell 7500) only work if these are set, regardless of wether we use the assp or not. */ outb(0, iobase + ASSP_CONTROL_B); outb(3, iobase + ASSP_CONTROL_A); /* M: Reserved bits... */ outb(0, iobase + ASSP_CONTROL_C); /* M: Disable ASSP, ASSP IRQ's and FM Port */ /* * set up wavecache */ for (i = 0; i < 16; i++) { /* Write 0 into the buffer area 0x1E0->1EF */ outw(0x01E0 + i, iobase + WC_INDEX); outw(0x0000, iobase + WC_DATA); /* The 1.10 test program seem to write 0 into the buffer area * 0x1D0-0x1DF too.*/ outw(0x01D0 + i, iobase + WC_INDEX); outw(0x0000, iobase + WC_DATA); } wave_set_register(chip, IDR7_WAVE_ROMRAM, (wave_get_register(chip, IDR7_WAVE_ROMRAM) & 0xFF00)); wave_set_register(chip, IDR7_WAVE_ROMRAM, wave_get_register(chip, IDR7_WAVE_ROMRAM) | 0x100); wave_set_register(chip, IDR7_WAVE_ROMRAM, wave_get_register(chip, IDR7_WAVE_ROMRAM) & ~0x200); wave_set_register(chip, IDR7_WAVE_ROMRAM, wave_get_register(chip, IDR7_WAVE_ROMRAM) | ~0x400); maestro_write(chip, IDR2_CRAM_DATA, 0x0000); /* Now back to the DirectSound stuff */ /* audio serial configuration.. ? */ maestro_write(chip, 0x08, 0xB004); maestro_write(chip, 0x09, 0x001B); maestro_write(chip, 0x0A, 0x8000); maestro_write(chip, 0x0B, 0x3F37); maestro_write(chip, 0x0C, 0x0098); /* parallel in, has something to do with recording :) */ maestro_write(chip, 0x0C, (maestro_read(chip, 0x0C) & ~0xF000) | 0x8000); /* parallel out */ maestro_write(chip, 0x0C, (maestro_read(chip, 0x0C) & ~0x0F00) | 0x0500); maestro_write(chip, 0x0D, 0x7632); /* Wave cache control on - test off, sg off, enable, enable extra chans 1Mb */ w = inw(iobase + WC_CONTROL); w &= ~0xFA00; /* Seems to be reserved? I don't know */ w |= 0xA000; /* reserved... I don't know */ w &= ~0x0200; /* Channels 56,57,58,59 as Extra Play,Rec Channel enable Seems to crash the Computer if enabled... */ w |= 0x0100; /* Wave Cache Operation Enabled */ w |= 0x0080; /* Channels 60/61 as Placback/Record enabled */ w &= ~0x0060; /* Clear Wavtable Size */ w |= 0x0020; /* Wavetable Size : 1MB */ /* Bit 4 is reserved */ w &= ~0x000C; /* DMA Stuff? I don't understand what the datasheet means */ /* Bit 1 is reserved */ w &= ~0x0001; /* Test Mode off */ outw(w, iobase + WC_CONTROL); /* Now clear the APU control ram */ for (i = 0; i < NR_APUS; i++) { for (w = 0; w < NR_APU_REGS; w++) apu_set_register(chip, i, w, 0); }}/* Enable IRQ's */static void snd_es1968_start_irq(struct es1968
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -