📄 cs46xx_lib.c
字号:
*/ snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN); /* * Wait until we've sampled input slots 3 and 4 as valid, meaning that * the codec is pumping ADC data across the AC-link. */ timeout = 150; while (timeout-- > 0) { /* * Read the input slot valid register and see if input slots 3 and * 4 are valid yet. */ if ((snd_cs46xx_peekBA0(chip, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4)) goto ok2; if (busywait) mdelay(10); else { set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((HZ+99)/100); } } snd_printk("create - never read ISV3 & ISV4 from AC'97\n"); snd_cs46xx_free(chip); return -EIO; ok2: /* * Now, assert valid frame and the slot 3 and 4 valid bits. This will * commense the transfer of digital audio data to the AC97 codec. */ snd_cs46xx_pokeBA0(chip, BA0_ACOSV, ACOSV_SLV3 | ACOSV_SLV4); /* * Power down the DAC and ADC. We will power them up (if) when we need * them. */ /* snd_cs46xx_pokeBA0(chip, BA0_AC97_POWERDOWN, 0x300); */ /* * Turn off the Processor by turning off the software clock enable flag in * the clock control register. */ /* tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1) & ~CLKCR1_SWCE; */ /* snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp); */ /* * Reset the processor. */ snd_cs46xx_reset(chip); /* * Download the image to the processor. */ if (snd_cs46xx_download_image(chip) < 0) { snd_printk("image download error\n"); snd_cs46xx_free(chip); return -EIO; } /* * Stop playback DMA. */ tmp = snd_cs46xx_peek(chip, BA1_PCTL); chip->play.ctl = tmp & 0xffff0000; snd_cs46xx_poke(chip, BA1_PCTL, tmp & 0x0000ffff); /* * Stop capture DMA. */ tmp = snd_cs46xx_peek(chip, BA1_CCTL); chip->capt.ctl = tmp & 0x0000ffff; snd_cs46xx_poke(chip, BA1_CCTL, tmp & 0xffff0000); snd_cs46xx_set_play_sample_rate(chip, 8000); snd_cs46xx_set_capture_sample_rate(chip, 8000); snd_cs46xx_proc_start(chip); /* * Enable interrupts on the part. */ snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_IEV | HICR_CHGM); tmp = snd_cs46xx_peek(chip, BA1_PFIE); tmp &= ~0x0000f03f; snd_cs46xx_poke(chip, BA1_PFIE, tmp); /* playback interrupt enable */ tmp = snd_cs46xx_peek(chip, BA1_CIE); tmp &= ~0x0000003f; tmp |= 0x00000001; snd_cs46xx_poke(chip, BA1_CIE, tmp); /* capture interrupt enable */ /* set the attenuation to 0dB */ snd_cs46xx_poke(chip, BA1_PVOL, 0x80008000); snd_cs46xx_poke(chip, BA1_CVOL, 0x80008000); return 0;}/* * AMP control - null AMP */ static void amp_none(cs46xx_t *chip, int change){ }/* * Crystal EAPD mode */ static void amp_voyetra(cs46xx_t *chip, int change){ /* Manage the EAPD bit on the Crystal 4297 and the Analog AD1885 */ int old = chip->amplifier; int oval, val; chip->amplifier += change; oval = snd_cs46xx_codec_read(chip, AC97_POWERDOWN); val = oval; if (chip->amplifier && !old) { /* Turn the EAPD amp on */ val |= 0x8000; } else if (old && !chip->amplifier) { /* Turn the EAPD amp off */ val &= ~0x8000; } if (val != oval) { snd_cs46xx_codec_write(chip, AC97_POWERDOWN, val); if (chip->eapd_switch) snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->eapd_switch->id); }}/* * Game Theatre XP card - EGPIO[2] is used to enable the external amp. */ static void amp_hercules(cs46xx_t *chip, int change){ int old = chip->amplifier; chip->amplifier += change; if (chip->amplifier && !old) { snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, EGPIODR_GPOE2); /* enable EGPIO2 output */ snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, EGPIOPTR_GPPT2); /* open-drain on output */ } else if (old && !chip->amplifier) { snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, 0); /* disable */ snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, 0); /* disable */ }}#if 0/* * Untested */ static void amp_voyetra_4294(cs46xx_t *chip, int change){ chip->amplifier += change; if (chip->amplifier) { /* Switch the GPIO pins 7 and 8 to open drain */ snd_cs46xx_codec_write(chip, 0x4C, snd_cs46xx_codec_read(chip, 0x4C) & 0xFE7F); snd_cs46xx_codec_write(chip, 0x4E, snd_cs46xx_codec_read(chip, 0x4E) | 0x0180); /* Now wake the AMP (this might be backwards) */ snd_cs46xx_codec_write(chip, 0x54, snd_cs46xx_codec_read(chip, 0x54) & ~0x0180); } else { snd_cs46xx_codec_write(chip, 0x54, snd_cs46xx_codec_read(chip, 0x54) | 0x0180); }}#endif/* * piix4 pci ids */#ifndef PCI_VENDOR_ID_INTEL#define PCI_VENDOR_ID_INTEL 0x8086#endif /* PCI_VENDOR_ID_INTEL */#ifndef PCI_DEVICE_ID_INTEL_82371AB_3#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113#endif /* PCI_DEVICE_ID_INTEL_82371AB_3 *//* * Handle the CLKRUN on a thinkpad. We must disable CLKRUN support * whenever we need to beat on the chip. * * The original idea and code for this hack comes from David Kaiser at * Linuxcare. Perhaps one day Crystal will document their chips well * enough to make them useful. */ static void clkrun_hack(cs46xx_t *chip, int change){ u16 control; int old; if (chip->acpi_dev == NULL) return; old = chip->amplifier; chip->amplifier += change; /* Read ACPI port */ control = inw(chip->acpi_port + 0x10); /* Flip CLKRUN off while running */ if (! chip->amplifier && old) outw(control | 0x2000, chip->acpi_port + 0x10); else if (chip->amplifier && ! old) outw(control & ~0x2000, chip->acpi_port + 0x10);} /* * detect intel piix4 */static void clkrun_init(cs46xx_t *chip){ u8 pp; chip->acpi_dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL); if (chip->acpi_dev == NULL) return; /* Not a thinkpad thats for sure */ /* Find the control port */ pci_read_config_byte(chip->acpi_dev, 0x41, &pp); chip->acpi_port = pp << 8;}/* * Card subid table */ struct cs_card_type{ u16 vendor; u16 id; char *name; void (*init)(cs46xx_t *); void (*amp)(cs46xx_t *, int); void (*active)(cs46xx_t *, int);};static struct cs_card_type __initdata cards[] = { {0x1489, 0x7001, "Genius Soundmaker 128 value", NULL, amp_none, NULL}, {0x5053, 0x3357, "Voyetra", NULL, amp_voyetra, NULL}, {0x1071, 0x6003, "Mitac MI6020/21", NULL, amp_voyetra, NULL}, {0x14AF, 0x0050, "Hercules Game Theatre XP", NULL, amp_hercules, NULL}, {0x1681, 0x0050, "Hercules Game Theatre XP", NULL, amp_hercules, NULL}, {0x1681, 0x0051, "Hercules Game Theatre XP", NULL, amp_hercules, NULL}, {0x1681, 0x0052, "Hercules Game Theatre XP", NULL, amp_hercules, NULL}, {0x1681, 0x0053, "Hercules Game Theatre XP", NULL, amp_hercules, NULL}, {0x1681, 0x0054, "Hercules Game Theatre XP", NULL, amp_hercules, NULL}, /* Not sure if the 570 needs the clkrun hack */ {PCI_VENDOR_ID_IBM, 0x0132, "Thinkpad 570", clkrun_init, NULL, clkrun_hack}, {PCI_VENDOR_ID_IBM, 0x0153, "Thinkpad 600X/A20/T20", clkrun_init, NULL, clkrun_hack}, {PCI_VENDOR_ID_IBM, 0x1010, "Thinkpad 600E (unsupported)", NULL, NULL, NULL}, {0, 0, "Card without SSID set", NULL, NULL, NULL }, {0, 0, NULL, NULL, NULL, NULL}};/* * APM support */#ifdef CONFIG_PMvoid snd_cs46xx_suspend(cs46xx_t *chip){ snd_card_t *card = chip->card; snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D3hot) goto __skip; snd_pcm_suspend_all(chip->pcm); // chip->ac97_powerdown = snd_cs46xx_codec_read(chip, AC97_POWER_CONTROL); // chip->ac97_general_purpose = snd_cs46xx_codec_read(chip, BA0_AC97_GENERAL_PURPOSE); snd_cs46xx_hw_stop(chip); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); __skip: snd_power_unlock(card);}void snd_cs46xx_resume(cs46xx_t *chip){ snd_card_t *card = chip->card; int amp_saved; snd_power_lock(card); if (card->power_state == SNDRV_CTL_POWER_D0) goto __skip; pci_enable_device(chip->pci); amp_saved = chip->amplifier; chip->amplifier = 0; chip->active_ctrl(chip, 1); /* force to on */ snd_cs46xx_chip_init(chip, 1);#if 0 snd_cs46xx_codec_write(chip, BA0_AC97_GENERAL_PURPOSE, chip->ac97_general_purpose); snd_cs46xx_codec_write(chip, AC97_POWER_CONTROL, chip->ac97_powerdown); mdelay(10); snd_cs46xx_codec_write(chip, BA0_AC97_POWERDOWN, chip->ac97_powerdown); mdelay(5);#endif snd_ac97_resume(chip->ac97); if (amp_saved) chip->amplifier_ctrl(chip, 1); /* try to turn on */ if (! amp_saved) { chip->amplifier = 1; chip->active_ctrl(chip, -1); } snd_power_change_state(card, SNDRV_CTL_POWER_D0); __skip: snd_power_unlock(card);}static int snd_cs46xx_set_power_state(snd_card_t *card, unsigned int power_state){ cs46xx_t *chip = snd_magic_cast(cs46xx_t, card->power_state_private_data, return -ENXIO); switch (power_state) { case SNDRV_CTL_POWER_D0: case SNDRV_CTL_POWER_D1: case SNDRV_CTL_POWER_D2: snd_cs46xx_resume(chip); break; case SNDRV_CTL_POWER_D3hot: case SNDRV_CTL_POWER_D3cold: snd_cs46xx_suspend(chip); break; default: return -EINVAL; } return 0;}#endif /* CONFIG_PM *//* */int __devinit snd_cs46xx_create(snd_card_t * card, struct pci_dev * pci, int external_amp, int thinkpad, cs46xx_t ** rchip){ cs46xx_t *chip; int err, idx; snd_cs46xx_region_t *region; struct cs_card_type *cp; u16 ss_card, ss_vendor; static snd_device_ops_t ops = { dev_free: snd_cs46xx_dev_free, }; *rchip = NULL; /* enable PCI device */ if ((err = pci_enable_device(pci)) < 0) return err; chip = snd_magic_kcalloc(cs46xx_t, 0, GFP_KERNEL); if (chip == NULL) return -ENOMEM; spin_lock_init(&chip->reg_lock); chip->card = card; chip->pci = pci; chip->play.hw_size = PAGE_SIZE; chip->capt.hw_size = PAGE_SIZE; chip->irq = -1; chip->ba0_addr = pci_resource_start(pci, 0); chip->ba1_addr = pci_resource_start(pci, 1); if (chip->ba0_addr == 0 || chip->ba0_addr == ~0 || chip->ba1_addr == 0 || chip->ba1_addr == ~0) { snd_cs46xx_free(chip); snd_printk("wrong address(es) - ba0 = 0x%lx, ba1 = 0x%lx\n", chip->ba0_addr, chip->ba1_addr); return -ENOMEM; } region = &chip->region.name.ba0; strcpy(region->name, "CS46xx_BA0"); region->base = chip->ba0_addr; region->size = CS46XX_BA0_SIZE; region = &chip->region.name.data0; strcpy(region->name, "CS46xx_BA1_data0"); region->base = chip->ba1_addr + BA1_SP_DMEM0; region->size = CS46XX_BA1_DATA0_SIZE; region = &chip->region.name.data1; strcpy(region->name, "CS46xx_BA1_data1"); region->base = chip->ba1_addr + BA1_SP_DMEM1; region->size = CS46XX_BA1_DATA1_SIZE; region = &chip->region.name.pmem; strcpy(region->name, "CS46xx_BA1_pmem"); region->base = chip->ba1_addr + BA1_SP_PMEM; region->size = CS46XX_BA1_PRG_SIZE; region = &chip->region.name.reg; strcpy(region->name, "CS46xx_BA1_reg"); region->base = chip->ba1_addr + BA1_SP_REG; region->size = CS46XX_BA1_REG_SIZE; /* set up amp and clkrun hack */ pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &ss_vendor); pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &ss_card); for (cp = &cards[0]; cp->name; cp++) { if (cp->vendor == ss_vendor && cp->id == ss_card) { snd_printd("hack for %s enabled\n", cp->name); if (cp->init) cp->init(chip); chip->amplifier_ctrl = cp->amp; chip->active_ctrl = cp->active; break; } } if (external_amp) { snd_printk("Crystal EAPD support forced on.\n"); chip->amplifier_ctrl = amp_voyetra; } if (thinkpad) { snd_printk("Activating CLKRUN hack for Thinkpad.\n"); chip->active_ctrl = clkrun_hack; clkrun_init(chip); } if (chip->amplifier_ctrl == NULL) chip->amplifier_ctrl = amp_none; if (chip->active_ctrl == NULL) chip->active_ctrl = amp_none; chip->active_ctrl(chip, 1); pci_set_master(pci); for (idx = 0; idx < 5; idx++) { region = &chip->region.idx[idx]; if ((region->resource = request_mem_region(region->base, region->size, region->name)) == NULL) { snd_cs46xx_free(chip); snd_printk("unable to request memory region 0x%lx-0x%lx\n", region->base, region->base + region->size - 1); return -EBUSY; } region->remap_addr = (unsigned long) ioremap_nocache(region->base, region->size); i
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -