es18xx.c
来自「linux 内核源代码」· C语言 代码 · 共 2,181 行 · 第 1/5 页
C
2,181 行
/* * The chipset specific mixer controls */static struct snd_kcontrol_new snd_es18xx_opt_speaker = ES18XX_SINGLE("PC Speaker Playback Volume", 0, 0x3c, 0, 7, 0);static struct snd_kcontrol_new snd_es18xx_opt_1869[] = {ES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),ES18XX_SINGLE("Video Playback Switch", 0, 0x7f, 0, 1, 0),ES18XX_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),ES18XX_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0)};static struct snd_kcontrol_new snd_es18xx_opt_1878 = ES18XX_DOUBLE("Video Playback Volume", 0, 0x68, 0x68, 4, 0, 15, 0);static struct snd_kcontrol_new snd_es18xx_opt_1879[] = {ES18XX_SINGLE("Video Playback Switch", 0, 0x71, 6, 1, 0),ES18XX_DOUBLE("Video Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),ES18XX_DOUBLE("Video Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0)};static struct snd_kcontrol_new snd_es18xx_pcm1_controls[] = {ES18XX_DOUBLE("PCM Playback Volume", 0, 0x14, 0x14, 4, 0, 15, 0),};static struct snd_kcontrol_new snd_es18xx_pcm2_controls[] = {ES18XX_DOUBLE("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0),ES18XX_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0)};static struct snd_kcontrol_new snd_es18xx_spatializer_controls[] = {ES18XX_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0),{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "3D Control - Switch", .info = snd_es18xx_info_spatializer_enable, .get = snd_es18xx_get_spatializer_enable, .put = snd_es18xx_put_spatializer_enable,}};static struct snd_kcontrol_new snd_es18xx_micpre1_control = ES18XX_SINGLE("Mic Boost (+26dB)", 0, 0xa9, 2, 1, 0);static struct snd_kcontrol_new snd_es18xx_micpre2_control =ES18XX_SINGLE("Mic Boost (+26dB)", 0, 0x7d, 3, 1, 0);static struct snd_kcontrol_new snd_es18xx_hw_volume_controls[] = {{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Hardware Master Playback Volume", .access = SNDRV_CTL_ELEM_ACCESS_READ, .info = snd_es18xx_info_hw_volume, .get = snd_es18xx_get_hw_volume,},{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Hardware Master Playback Switch", .access = SNDRV_CTL_ELEM_ACCESS_READ, .info = snd_es18xx_info_hw_switch, .get = snd_es18xx_get_hw_switch,},ES18XX_SINGLE("Hardware Master Volume Split", 0, 0x64, 7, 1, 0),};static int __devinit snd_es18xx_config_read(struct snd_es18xx *chip, unsigned char reg){ int data; unsigned long flags; spin_lock_irqsave(&chip->ctrl_lock, flags); outb(reg, chip->ctrl_port); data = inb(chip->ctrl_port + 1); spin_unlock_irqrestore(&chip->ctrl_lock, flags); return data;}static void __devinit snd_es18xx_config_write(struct snd_es18xx *chip, unsigned char reg, unsigned char data){ /* No need for spinlocks, this function is used only in otherwise protected init code */ outb(reg, chip->ctrl_port); outb(data, chip->ctrl_port + 1);#ifdef REG_DEBUG snd_printk(KERN_DEBUG "Config reg %02x set to %02x\n", reg, data);#endif}static int __devinit snd_es18xx_initialize(struct snd_es18xx *chip){ int mask = 0; /* enable extended mode */ snd_es18xx_dsp_command(chip, 0xC6); /* Reset mixer registers */ snd_es18xx_mixer_write(chip, 0x00, 0x00); /* Audio 1 DMA demand mode (4 bytes/request) */ snd_es18xx_write(chip, 0xB9, 2); if (chip->caps & ES18XX_CONTROL) { /* Hardware volume IRQ */ snd_es18xx_config_write(chip, 0x27, chip->irq); if (chip->fm_port > 0 && chip->fm_port != SNDRV_AUTO_PORT) { /* FM I/O */ snd_es18xx_config_write(chip, 0x62, chip->fm_port >> 8); snd_es18xx_config_write(chip, 0x63, chip->fm_port & 0xff); } if (chip->mpu_port > 0 && chip->mpu_port != SNDRV_AUTO_PORT) { /* MPU-401 I/O */ snd_es18xx_config_write(chip, 0x64, chip->mpu_port >> 8); snd_es18xx_config_write(chip, 0x65, chip->mpu_port & 0xff); /* MPU-401 IRQ */ snd_es18xx_config_write(chip, 0x28, chip->irq); } /* Audio1 IRQ */ snd_es18xx_config_write(chip, 0x70, chip->irq); /* Audio2 IRQ */ snd_es18xx_config_write(chip, 0x72, chip->irq); /* Audio1 DMA */ snd_es18xx_config_write(chip, 0x74, chip->dma1); /* Audio2 DMA */ snd_es18xx_config_write(chip, 0x75, chip->dma2); /* Enable Audio 1 IRQ */ snd_es18xx_write(chip, 0xB1, 0x50); /* Enable Audio 2 IRQ */ snd_es18xx_mixer_write(chip, 0x7A, 0x40); /* Enable Audio 1 DMA */ snd_es18xx_write(chip, 0xB2, 0x50); /* Enable MPU and hardware volume interrupt */ snd_es18xx_mixer_write(chip, 0x64, 0x42); } else { int irqmask, dma1mask, dma2mask; switch (chip->irq) { case 2: case 9: irqmask = 0; break; case 5: irqmask = 1; break; case 7: irqmask = 2; break; case 10: irqmask = 3; break; default: snd_printk(KERN_ERR "invalid irq %d\n", chip->irq); return -ENODEV; } switch (chip->dma1) { case 0: dma1mask = 1; break; case 1: dma1mask = 2; break; case 3: dma1mask = 3; break; default: snd_printk(KERN_ERR "invalid dma1 %d\n", chip->dma1); return -ENODEV; } switch (chip->dma2) { case 0: dma2mask = 0; break; case 1: dma2mask = 1; break; case 3: dma2mask = 2; break; case 5: dma2mask = 3; break; default: snd_printk(KERN_ERR "invalid dma2 %d\n", chip->dma2); return -ENODEV; } /* Enable and set Audio 1 IRQ */ snd_es18xx_write(chip, 0xB1, 0x50 | (irqmask << 2)); /* Enable and set Audio 1 DMA */ snd_es18xx_write(chip, 0xB2, 0x50 | (dma1mask << 2)); /* Set Audio 2 DMA */ snd_es18xx_mixer_bits(chip, 0x7d, 0x07, 0x04 | dma2mask); /* Enable Audio 2 IRQ and DMA Set capture mixer input */ snd_es18xx_mixer_write(chip, 0x7A, 0x68); /* Enable and set hardware volume interrupt */ snd_es18xx_mixer_write(chip, 0x64, 0x06); if (chip->mpu_port > 0 && chip->mpu_port != SNDRV_AUTO_PORT) { /* MPU401 share irq with audio Joystick enabled FM enabled */ snd_es18xx_mixer_write(chip, 0x40, 0x43 | (chip->mpu_port & 0xf0) >> 1); } snd_es18xx_mixer_write(chip, 0x7f, ((irqmask + 1) << 1) | 0x01); } if (chip->caps & ES18XX_NEW_RATE) { /* Change behaviour of register A1 4x oversampling 2nd channel DAC asynchronous */ snd_es18xx_mixer_write(chip, 0x71, 0x32); } if (!(chip->caps & ES18XX_PCM2)) { /* Enable DMA FIFO */ snd_es18xx_write(chip, 0xB7, 0x80); } if (chip->caps & ES18XX_SPATIALIZER) { /* Set spatializer parameters to recommended values */ snd_es18xx_mixer_write(chip, 0x54, 0x8f); snd_es18xx_mixer_write(chip, 0x56, 0x95); snd_es18xx_mixer_write(chip, 0x58, 0x94); snd_es18xx_mixer_write(chip, 0x5a, 0x80); } /* Flip the "enable I2S" bits for those chipsets that need it */ switch (chip->version) { case 0x1879: //Leaving I2S enabled on the 1879 screws up the PCM playback (rate effected somehow) //so a Switch control has been added to toggle this 0x71 bit on/off: //snd_es18xx_mixer_bits(chip, 0x71, 0x40, 0x40); /* Note: we fall through on purpose here. */ case 0x1878: snd_es18xx_config_write(chip, 0x29, snd_es18xx_config_read(chip, 0x29) | 0x40); break; } /* Mute input source */ if (chip->caps & ES18XX_MUTEREC) mask = 0x10; if (chip->caps & ES18XX_RECMIX) snd_es18xx_mixer_write(chip, 0x1c, 0x05 | mask); else { snd_es18xx_mixer_write(chip, 0x1c, 0x00 | mask); snd_es18xx_write(chip, 0xb4, 0x00); }#ifndef AVOID_POPS /* Enable PCM output */ snd_es18xx_dsp_command(chip, 0xD1);#endif return 0;}static int __devinit snd_es18xx_identify(struct snd_es18xx *chip){ int hi,lo; /* reset */ if (snd_es18xx_reset(chip) < 0) { snd_printk(KERN_ERR "reset at 0x%lx failed!!!\n", chip->port); return -ENODEV; } snd_es18xx_dsp_command(chip, 0xe7); hi = snd_es18xx_dsp_get_byte(chip); if (hi < 0) { return hi; } lo = snd_es18xx_dsp_get_byte(chip); if ((lo & 0xf0) != 0x80) { return -ENODEV; } if (hi == 0x48) { chip->version = 0x488; return 0; } if (hi != 0x68) { return -ENODEV; } if ((lo & 0x0f) < 8) { chip->version = 0x688; return 0; } outb(0x40, chip->port + 0x04); udelay(10); hi = inb(chip->port + 0x05); udelay(10); lo = inb(chip->port + 0x05); if (hi != lo) { chip->version = hi << 8 | lo; chip->ctrl_port = inb(chip->port + 0x05) << 8; udelay(10); chip->ctrl_port += inb(chip->port + 0x05); if ((chip->res_ctrl_port = request_region(chip->ctrl_port, 8, "ES18xx - CTRL")) == NULL) { snd_printk(KERN_ERR PFX "unable go grab port 0x%lx\n", chip->ctrl_port); return -EBUSY; } return 0; } /* If has Hardware volume */ if (snd_es18xx_mixer_writable(chip, 0x64, 0x04)) { /* If has Audio2 */ if (snd_es18xx_mixer_writable(chip, 0x70, 0x7f)) { /* If has volume count */ if (snd_es18xx_mixer_writable(chip, 0x64, 0x20)) { chip->version = 0x1887; } else { chip->version = 0x1888; } } else { chip->version = 0x1788; } } else chip->version = 0x1688; return 0;}static int __devinit snd_es18xx_probe(struct snd_es18xx *chip){ if (snd_es18xx_identify(chip) < 0) { snd_printk(KERN_ERR PFX "[0x%lx] ESS chip not found\n", chip->port); return -ENODEV; } switch (chip->version) { case 0x1868: chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_CONTROL; break; case 0x1869: chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_MONO | ES18XX_MUTEREC | ES18XX_CONTROL | ES18XX_HWV; break; case 0x1878: chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_I2S | ES18XX_CONTROL; break; case 0x1879: chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_I2S | ES18XX_CONTROL | ES18XX_HWV; break; case 0x1887: chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME; break; case 0x1888: chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME; break; default: snd_printk(KERN_ERR "[0x%lx] unsupported chip ES%x\n", chip->port, chip->version); return -ENODEV; } snd_printd("[0x%lx] ESS%x chip found\n", chip->port, chip->version); if (chip->dma1 == chip->dma2) chip->caps &= ~(ES18XX_PCM2 | ES18XX_DUPLEX_SAME); return snd_es18xx_initialize(chip);}static struct snd_pcm_ops snd_es18xx_playback_ops = { .open = snd_es18xx_playback_open, .close = snd_es18xx_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_es18xx_playback_hw_params, .hw_free = snd_es18xx_pcm_hw_free, .prepare = snd_es18xx_playback_prepare, .trigger = snd_es18xx_playback_trigger, .pointer = snd_es18xx_playback_pointer,};static struct snd_pcm_ops snd_es18xx_capture_ops = { .open = snd_es18xx_capture_open, .close = snd_es18xx_capture_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_es18xx_capture_hw_params, .hw_free = snd_es18xx_pcm_hw_free, .prepare = snd_es18xx_capture_prepare, .trigger = snd_es18xx_capture_trigger, .pointer = snd_es18xx_capture_pointer,};static int __devinit snd_es18xx_pcm(struct snd_es18xx *chip, int device, struct snd_pcm ** rpcm){ struct snd_pcm *pcm; char str[16]; int err; if (rpcm) *rpcm = NULL; sprintf(str, "ES%x", chip->version); if (chip->caps & ES18XX_PCM2) err = snd_pcm_new(chip->card, str, device, 2, 1, &pcm); else err = snd_pcm_new(chip->card, str, device, 1, 1, &pcm); if (err < 0) return err; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es18xx_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es18xx_capture_ops); /* global setup */ pcm->private_data = chip; pcm->info_flags = 0; if (chip->caps & ES18XX_DUPLEX_SAME) pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX; if (! (chip->caps & ES18XX_PCM2)) pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX; sprintf(pcm->name, "ESS AudioDrive ES%x", chip->version); chip->pcm = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_isa_data(), 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); if (rpcm) *rpcm = pcm; return 0;}/* Power Management support functions */#ifdef CONFIG_PMstatic int snd_es18xx_suspend(struct snd_card *card, pm_message_t state){ struct snd_audiodrive *acard = card->private_data; struct snd_es18xx *chip = acard->chip; snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot); snd_pcm_suspend_all(chip->pcm); /* power down */ chip->pm_reg = (unsigned char)snd_es18xx_read(chip, ES18XX_PM); chip->pm_reg |= (ES18XX_PM_FM | ES18XX_PM_SUS); snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg); snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_SUS);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?