ac97_codec.c
来自「优龙2410linux2.6.8内核源代码」· C语言 代码 · 共 1,915 行 · 第 1/5 页
C
1,915 行
if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_eapd, ac97))) < 0) return err; } return 0;}static int snd_ac97_modem_build(snd_card_t * card, ac97_t * ac97){ /* TODO */ //printk("AC97_GPIO_CFG = %x\n",snd_ac97_read(ac97,AC97_GPIO_CFG)); snd_ac97_write(ac97, AC97_GPIO_CFG, 0xffff & ~(AC97_GPIO_LINE1_OH)); snd_ac97_write(ac97, AC97_GPIO_POLARITY, 0xffff & ~(AC97_GPIO_LINE1_OH)); snd_ac97_write(ac97, AC97_GPIO_STICKY, 0xffff); snd_ac97_write(ac97, AC97_GPIO_WAKEUP, 0x0); snd_ac97_write(ac97, AC97_MISC_AFE, 0x0); return 0;}static int snd_ac97_test_rate(ac97_t *ac97, int reg, int shadow_reg, int rate){ unsigned short val; unsigned int tmp; tmp = ((unsigned int)rate * ac97->bus->clock) / 48000; snd_ac97_write_cache(ac97, reg, tmp & 0xffff); if (shadow_reg) snd_ac97_write_cache(ac97, shadow_reg, tmp & 0xffff); val = snd_ac97_read(ac97, reg); return val == (tmp & 0xffff);}static void snd_ac97_determine_rates(ac97_t *ac97, int reg, int shadow_reg, unsigned int *r_result){ unsigned int result = 0; /* test a non-standard rate */ if (snd_ac97_test_rate(ac97, reg, shadow_reg, 11000)) result |= SNDRV_PCM_RATE_CONTINUOUS; /* let's try to obtain standard rates */ if (snd_ac97_test_rate(ac97, reg, shadow_reg, 8000)) result |= SNDRV_PCM_RATE_8000; if (snd_ac97_test_rate(ac97, reg, shadow_reg, 11025)) result |= SNDRV_PCM_RATE_11025; if (snd_ac97_test_rate(ac97, reg, shadow_reg, 16000)) result |= SNDRV_PCM_RATE_16000; if (snd_ac97_test_rate(ac97, reg, shadow_reg, 22050)) result |= SNDRV_PCM_RATE_22050; if (snd_ac97_test_rate(ac97, reg, shadow_reg, 32000)) result |= SNDRV_PCM_RATE_32000; if (snd_ac97_test_rate(ac97, reg, shadow_reg, 44100)) result |= SNDRV_PCM_RATE_44100; if (snd_ac97_test_rate(ac97, reg, shadow_reg, 48000)) result |= SNDRV_PCM_RATE_48000; *r_result = result;}/* check AC97_SPDIF register to accept which sample rates */static unsigned int snd_ac97_determine_spdif_rates(ac97_t *ac97){ unsigned int result = 0; int i; static unsigned short ctl_bits[] = { AC97_SC_SPSR_44K, AC97_SC_SPSR_32K, AC97_SC_SPSR_48K }; static unsigned int rate_bits[] = { SNDRV_PCM_RATE_44100, SNDRV_PCM_RATE_32000, SNDRV_PCM_RATE_48000 }; for (i = 0; i < (int)ARRAY_SIZE(ctl_bits); i++) { snd_ac97_update_bits(ac97, AC97_SPDIF, AC97_SC_SPSR_MASK, ctl_bits[i]); if ((snd_ac97_read(ac97, AC97_SPDIF) & AC97_SC_SPSR_MASK) == ctl_bits[i]) result |= rate_bits[i]; } return result;}void snd_ac97_get_name(ac97_t *ac97, unsigned int id, char *name, int modem){ const ac97_codec_id_t *pid; sprintf(name, "0x%x %c%c%c", id, printable(id >> 24), printable(id >> 16), printable(id >> 8)); for (pid = snd_ac97_codec_id_vendors; pid->id; pid++) if (pid->id == (id & pid->mask)) { strcpy(name, pid->name); if (ac97) { if (!modem && pid->patch) pid->patch(ac97); else if (modem && pid->mpatch) pid->mpatch(ac97); } goto __vendor_ok; } return; __vendor_ok: for (pid = snd_ac97_codec_ids; pid->id; pid++) if (pid->id == (id & pid->mask)) { strcat(name, " "); strcat(name, pid->name); if (pid->mask != 0xffffffff) sprintf(name + strlen(name), " rev %d", id & ~pid->mask); if (ac97) { if (!modem && pid->patch) pid->patch(ac97); else if (modem && pid->mpatch) pid->mpatch(ac97); } return; } sprintf(name + strlen(name), " id %x", id & 0xff);}/* wait for a while until registers are accessible after RESET * return 0 if ok, negative not ready */static int ac97_reset_wait(ac97_t *ac97, int timeout, int with_modem){ unsigned long end_time; end_time = jiffies + timeout; do { unsigned short ext_mid; /* use preliminary reads to settle the communication */ snd_ac97_read(ac97, AC97_RESET); snd_ac97_read(ac97, AC97_VENDOR_ID1); snd_ac97_read(ac97, AC97_VENDOR_ID2); /* modem? */ if (with_modem) { ext_mid = snd_ac97_read(ac97, AC97_EXTENDED_MID); if (ext_mid != 0xffff && (ext_mid & 1) != 0) return 0; } /* because the PCM or MASTER volume registers can be modified, * the REC_GAIN register is used for tests */ /* test if we can write to the record gain volume register */ snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a05); if (snd_ac97_read(ac97, AC97_REC_GAIN) == 0x8a05) return 0; set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ/100); } while (time_after_eq(end_time, jiffies)); return -ENODEV;}/** * snd_ac97_bus - create an AC97 bus component * @card: the card instance * @_bus: the template of AC97 bus, callbacks and * the private data. * @rbus: the pointer to store the new AC97 bus instance. * * Creates an AC97 bus component. An ac97_bus_t instance is newly * allocated and initialized from the template (_bus). * * The template must include the valid callbacks (at least read and * write), the bus number (num), and the private data (private_data). * The other callbacks, wait and reset, are not mandatory. * * The clock is set to 48000. If another clock is needed, set * bus->clock manually. * * The AC97 bus instance is registered as a low-level device, so you don't * have to release it manually. * * Returns zero if successful, or a negative error code on failure. */int snd_ac97_bus(snd_card_t * card, ac97_bus_t * _bus, ac97_bus_t ** rbus){ int err; ac97_bus_t *bus; static snd_device_ops_t ops = { .dev_free = snd_ac97_bus_dev_free, }; snd_assert(card != NULL, return -EINVAL); snd_assert(_bus != NULL && rbus != NULL, return -EINVAL); bus = snd_magic_kmalloc(ac97_bus_t, 0, GFP_KERNEL); if (bus == NULL) return -ENOMEM; *bus = *_bus; bus->card = card; if (bus->clock == 0) bus->clock = 48000; spin_lock_init(&bus->bus_lock); snd_ac97_bus_proc_init(bus); if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, bus, &ops)) < 0) { snd_ac97_bus_free(bus); return err; } *rbus = bus; return 0;}/** * snd_ac97_mixer - create an Codec97 component * @bus: the AC97 bus which codec is attached to * @_ac97: the template of ac97, including index, callbacks and * the private data. * @rac97: the pointer to store the new ac97 instance. * * Creates an Codec97 component. An ac97_t instance is newly * allocated and initialized from the template (_ac97). The codec * is then initialized by the standard procedure. * * The template must include the valid callbacks (at least read and * write), the codec number (num) and address (addr), and the private * data (private_data). The other callbacks, wait and reset, are not * mandatory. * * The ac97 instance is registered as a low-level device, so you don't * have to release it manually. * * Returns zero if successful, or a negative error code on failure. */int snd_ac97_mixer(ac97_bus_t * bus, ac97_t * _ac97, ac97_t ** rac97){ int err; ac97_t *ac97; snd_card_t *card; char name[64]; unsigned long end_time; unsigned int reg; static snd_device_ops_t ops = { .dev_free = snd_ac97_dev_free, }; snd_assert(rac97 != NULL, return -EINVAL); *rac97 = NULL; snd_assert(bus != NULL && _ac97 != NULL, return -EINVAL); snd_assert(_ac97->num < 4 && bus->codec[_ac97->num] == NULL, return -EINVAL); card = bus->card; ac97 = snd_magic_kmalloc(ac97_t, 0, GFP_KERNEL); if (ac97 == NULL) return -ENOMEM; *ac97 = *_ac97; ac97->bus = bus; bus->codec[ac97->num] = ac97; spin_lock_init(&ac97->reg_lock); if (ac97->pci) { pci_read_config_word(ac97->pci, PCI_SUBSYSTEM_VENDOR_ID, &ac97->subsystem_vendor); pci_read_config_word(ac97->pci, PCI_SUBSYSTEM_ID, &ac97->subsystem_device); } if (bus->reset) { bus->reset(ac97); goto __access_ok; } snd_ac97_write(ac97, AC97_RESET, 0); /* reset to defaults */ if (bus->wait) bus->wait(ac97); else { udelay(50); if (ac97_reset_wait(ac97, HZ/2, 0) < 0 && ac97_reset_wait(ac97, HZ/2, 1) < 0) { snd_printk(KERN_WARNING "AC'97 %d does not respond - RESET\n", ac97->num); /* proceed anyway - it's often non-critical */ } } __access_ok: ac97->id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16; ac97->id |= snd_ac97_read(ac97, AC97_VENDOR_ID2); if (ac97->id == 0x00000000 || ac97->id == 0xffffffff) { snd_printk(KERN_ERR "AC'97 %d access is not valid [0x%x], removing mixer.\n", ac97->num, ac97->id); snd_ac97_free(ac97); return -EIO; } /* AC97 audio codec chip revision detection. */ /* Currently only Realtek ALC650 detection implemented. */ switch(ac97->id & 0xfffffff0) { case 0x414c4720: /* ALC650 */ reg = snd_ac97_read(ac97, AC97_ALC650_REVISION); if (((reg & 0x3f) >= 0) && ((reg & 0x3f) < 3)) ac97->id = 0x414c4720; /* Old version */ else if (((reg & 0x3f) >= 3) && ((reg & 0x3f) < 0x10)) ac97->id = 0x414c4721; /* D version */ else if ((reg&0x30) == 0x10) ac97->id = 0x414c4722; /* E version */ else if ((reg&0x30) == 0x20) ac97->id = 0x414c4723; /* F version */ } /* test for AC'97 */ if (!(ac97->scaps & AC97_SCAP_SKIP_AUDIO) && !(ac97->scaps & AC97_SCAP_AUDIO)) { /* test if we can write to the record gain volume register */ snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a06); if ((err = snd_ac97_read(ac97, AC97_REC_GAIN)) == 0x8a06) ac97->scaps |= AC97_SCAP_AUDIO; } if (ac97->scaps & AC97_SCAP_AUDIO) { ac97->caps = snd_ac97_read(ac97, AC97_RESET); ac97->ext_id = snd_ac97_read(ac97, AC97_EXTENDED_ID); if (ac97->ext_id == 0xffff) /* invalid combination */ ac97->ext_id = 0; } /* test for MC'97 */ if (!(ac97->scaps & AC97_SCAP_SKIP_MODEM) && !(ac97->scaps & AC97_SCAP_MODEM)) { ac97->ext_mid = snd_ac97_read(ac97, AC97_EXTENDED_MID); if (ac97->ext_mid == 0xffff) /* invalid combination */ ac97->ext_mid = 0; if (ac97->ext_mid & 1) ac97->scaps |= AC97_SCAP_MODEM; } if (!ac97_is_audio(ac97) && !ac97_is_modem(ac97)) { if (!(ac97->scaps & (AC97_SCAP_SKIP_AUDIO|AC97_SCAP_SKIP_MODEM))) snd_printk(KERN_ERR "AC'97 %d access error (not audio or modem codec)\n", ac97->num); snd_ac97_free(ac97); return -EACCES; } if (bus->reset) // FIXME: always skipping? goto __ready_ok; /* FIXME: add powerdown control */ if (ac97_is_audio(ac97)) { /* nothing should be in powerdown mode */ snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0); snd_ac97_write_cache(ac97, AC97_RESET, 0); /* reset to defaults */ udelay(100); /* nothing should be in powerdown mode */ snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0); snd_ac97_write_cache(ac97, AC97_GENERAL_PURPOSE, 0); end_time = jiffies + (HZ / 10); do { if ((snd_ac97_read(ac97, AC97_POWERDOWN) & 0x0f) == 0x0f) goto __ready_ok; set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ/10); } while (time_after_eq(end_time, jiffies)); snd_printk(KERN_ERR "AC'97 %d analog subsections not ready\n", ac97->num); } /* FIXME: add powerdown control */ if (ac97_is_modem(ac97)) { unsigned char tmp; /* nothing should be in powerdown mode */ /* note: it's important to set the rate at first */ tmp = AC97_MEA_GPIO; if (ac97->ext_mid & AC97_MEI_LINE1) { snd_ac97_write_cache(ac97, AC97_LINE1_RATE, 12000); tmp |= AC97_MEA_ADC1 | AC97_MEA_DAC1; } if (ac97->ext_mid & AC97_MEI_LINE2) { snd_ac97_write_cache(ac97, AC97_LINE2_RATE, 12000); tmp |= AC97_MEA_ADC2 | AC97_MEA_DAC2; } if (ac97->ext_mid & AC97_MEI_HANDSET) { snd_ac97_write_cache(ac97, AC97_HANDSET_RATE, 12000); tmp |= AC97_MEA_HADC | AC97_MEA_HDAC; } snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0xff00 & ~(tmp << 8)); udelay(100); /* nothing should be in powerdown mode */ snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0xff00 & ~(tmp << 8)); end_time = jiffies + (HZ / 10); do { if ((snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS) & tmp) == tmp) goto __ready_ok; set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ/10); } while (time_after_eq(end_time, jiffies)); snd_printk(KERN_ERR "MC'97 %d converters and GPIO not ready (0x%x)\n", ac97->num, snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS)); } __ready_ok: if (ac97_is_audio(ac97)) ac97->addr = (ac97->ext_id & AC97_EI_ADDR_MASK) >> AC97_EI_ADDR_SHIFT; else ac97->addr = (ac97->ext_mid & AC97_MEI_ADDR_MASK) >> AC97_MEI_ADDR_SHIFT; if (ac97->ext_id & 0x0189) /* L/R, MIC, SDAC, LDAC VRA support */ snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, ac97->ext_id & 0x0189); if (ac97->ext_id & AC97_EI_VRA) { /* VRA support */ snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, 0, &ac97->rates[AC97_RATES_FRONT_DAC]); snd_ac97_determine_rates(ac97,
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?