📄 ac97_codec.c
字号:
for (idx = 0; idx < 2; idx++) if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_pcm[idx], ac97))) < 0) return err; } snd_ac97_write_cache(ac97, AC97_PCM, 0x9f1f); /* build Capture controls */ for (idx = 0; idx < 3; idx++) if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_capture[idx], ac97))) < 0) return err; snd_ac97_write_cache(ac97, AC97_REC_SEL, 0x0000); snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x0000); /* build MIC Capture controls */ if (snd_ac97_try_volume_mix(ac97, AC97_REC_GAIN_MIC)) { for (idx = 0; idx < 2; idx++) if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_capture[idx], ac97))) < 0) return err; snd_ac97_write_cache(ac97, AC97_REC_GAIN_MIC, 0x0000); } /* build PCM out path & mute control */ if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 15)) { if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_PCM_OUT], ac97))) < 0) return err; } /* build Simulated Stereo Enhancement control */ if (ac97->caps & 0x0008) { if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_STEREO_ENHANCEMENT], ac97))) < 0) return err; } /* build 3D Stereo Enhancement control */ if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 13)) { if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_3D], ac97))) < 0) return err; } /* build Loudness control */ if (ac97->caps & 0x0020) { if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOUDNESS], ac97))) < 0) return err; } /* build Mono output select control */ if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 9)) { if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MONO], ac97))) < 0) return err; } /* build Mic select control */ if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 8)) { if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MIC], ac97))) < 0) return err; } /* build ADC/DAC loopback control */ if (enable_loopback && snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 7)) { if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOOPBACK], ac97))) < 0) return err; } snd_ac97_write_cache(ac97, AC97_GENERAL_PURPOSE, 0x0000); /* build 3D controls */ switch (ac97->id) { case AC97_ID_STAC9708: if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0) return err; strcpy(kctl->id.name, "3D Control Sigmatel - Depth"); kctl->private_value = AC97_3D_CONTROL | (3 << 16); if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0) return err; strcpy(kctl->id.name, "3D Control Sigmatel - Rear Depth"); kctl->private_value = AC97_3D_CONTROL | (2 << 8) | (3 << 16); snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); break; case AC97_ID_STAC9700: case AC97_ID_STAC9721: case AC97_ID_STAC9744: case AC97_ID_STAC9756: if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0) return err; strcpy(kctl->id.name, "3D Control Sigmatel - Depth"); kctl->private_value = AC97_3D_CONTROL | (3 << 16); snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); break; default: if (snd_ac97_try_volume_mix(ac97, AC97_3D_CONTROL)) { unsigned short val; val = 0x0707; snd_ac97_write(ac97, AC97_3D_CONTROL, val); val = snd_ac97_read(ac97, AC97_3D_CONTROL); val = val == 0x0606; if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0) return err; if (val) kctl->private_value = AC97_3D_CONTROL | (9 << 8) | (7 << 16); if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[1], ac97))) < 0) return err; if (val) kctl->private_value = AC97_3D_CONTROL | (1 << 8) | (7 << 16); snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); } } /* build S/PDIF controls */ if (ac97->ext_id & 0x0004) { for (idx = 0; idx < 5; idx++) if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_spdif[idx], ac97))) < 0) return err; /* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original */ snd_ac97_write_cache(ac97, AC97_SPDIF, 0x2a20); ac97->spdif_status = SNDRV_PCM_DEFAULT_CON_SPDIF; } /* build Sigmatel specific controls */ switch (ac97->id) { case AC97_ID_STAC9700: case AC97_ID_STAC9708: case AC97_ID_STAC9721: case AC97_ID_STAC9744: case AC97_ID_STAC9756: snd_ac97_write_cache_test(ac97, AC97_SIGMATEL_ANALOG, snd_ac97_read(ac97, AC97_SIGMATEL_ANALOG) & ~0x0003); if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_ANALOG, 1)) if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_sigmatel_controls[0], ac97))) < 0) return err; if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_ANALOG, 0)) if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_sigmatel_controls[1], ac97))) < 0) return err; default: /* nothing */ break; } if (snd_ac97_try_bit(ac97, AC97_POWERDOWN, 15)) { if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_eapd, ac97))) < 0) return err; } return 0;}static int snd_ac97_test_rate(ac97_t *ac97, int reg, int rate){ unsigned short val; unsigned int tmp; tmp = ((unsigned int)rate * ac97->clock) / 48000; snd_ac97_write_cache_test(ac97, reg, tmp & 0xffff); val = snd_ac97_read(ac97, reg); return val == (tmp & 0xffff);}static void snd_ac97_determine_rates(ac97_t *ac97, int reg, unsigned int *r_result){ unsigned int result = 0; /* test a non-standard rate */ if (snd_ac97_test_rate(ac97, reg, 11000)) result |= SNDRV_PCM_RATE_CONTINUOUS; /* let's try to obtain standard rates */ if (snd_ac97_test_rate(ac97, reg, 8000)) result |= SNDRV_PCM_RATE_8000; if (snd_ac97_test_rate(ac97, reg, 11025)) result |= SNDRV_PCM_RATE_11025; if (snd_ac97_test_rate(ac97, reg, 16000)) result |= SNDRV_PCM_RATE_16000; if (snd_ac97_test_rate(ac97, reg, 22050)) result |= SNDRV_PCM_RATE_22050; if (snd_ac97_test_rate(ac97, reg, 32000)) result |= SNDRV_PCM_RATE_32000; if (snd_ac97_test_rate(ac97, reg, 44100)) result |= SNDRV_PCM_RATE_44100; if (snd_ac97_test_rate(ac97, reg, 48000)) result |= SNDRV_PCM_RATE_48000; *r_result = result;}static void snd_ac97_get_name(ac97_t *ac97, unsigned int id, char *name){ 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 && pid->patch) pid->patch(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 && pid->patch) pid->patch(ac97); return; } sprintf(name + strlen(name), " (%x)", id & 0xff);}int snd_ac97_mixer(snd_card_t * card, ac97_t * _ac97, ac97_t ** rac97){ int err; ac97_t *ac97; char name[64]; signed long end_time; static snd_device_ops_t ops = { dev_free: snd_ac97_dev_free, }; snd_assert(rac97 != NULL, return -EINVAL); *rac97 = NULL; snd_assert(card != NULL && _ac97 != NULL, return -EINVAL); ac97 = snd_magic_kcalloc(ac97_t, 0, GFP_KERNEL); if (ac97 == NULL) return -ENOMEM; *ac97 = *_ac97; ac97->card = card; spin_lock_init(&ac97->reg_lock); snd_ac97_write(ac97, AC97_RESET, 0); /* reset to defaults */ if (ac97->wait) ac97->wait(ac97); else { udelay(50); /* it's necessary to wait awhile until registers are accessible after RESET */ /* because the PCM or MASTER volume registers can be modified, */ /* the REC_GAIN register is used for tests */ end_time = jiffies + HZ; do { /* 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); /* test if we can write to the PCM volume register */ snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a05); if ((err = snd_ac97_read(ac97, AC97_REC_GAIN)) == 0x8a05) goto __access_ok; set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ/100); } while (end_time - (signed long)jiffies >= 0); snd_printd("AC'97 %d:%d does not respond - RESET [REC_GAIN = 0x%x]\n", ac97->num, ac97->addr, err); snd_ac97_free(ac97); return -ENXIO; } __access_ok: ac97->caps = snd_ac97_read(ac97, AC97_RESET); ac97->id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16; ac97->id |= snd_ac97_read(ac97, AC97_VENDOR_ID2); ac97->ext_id = snd_ac97_read(ac97, AC97_EXTENDED_ID); if (ac97->ext_id == 0xffff) /* invalid combination */ ac97->ext_id = 0; if (ac97->id == 0x00000000 || ac97->id == 0xffffffff) { snd_printk("AC'97 %d:%d access is not valid [0x%x], removing mixer.\n", ac97->num, ac97->addr, ac97->id); snd_ac97_free(ac97); return -EIO; } /* FIXME: add powerdown control */ /* nothing should be in powerdown mode */ snd_ac97_write_cache_test(ac97, AC97_POWERDOWN, 0); snd_ac97_write_cache_test(ac97, AC97_RESET, 0); /* reset to defaults */ udelay(100); /* nothing should be in powerdown mode */ snd_ac97_write_cache_test(ac97, AC97_POWERDOWN, 0); snd_ac97_write_cache_test(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 (end_time - (signed long)jiffies >= 0); snd_printk("AC'97 %d:%d analog subsections not ready\n", ac97->num, ac97->addr); __ready_ok: if (ac97->clock == 0) ac97->clock = 48000; /* standard value */ 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 & 0x0001) { /* VRA support */ snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, &ac97->rates_front_dac); snd_ac97_determine_rates(ac97, AC97_PCM_LR_ADC_RATE, &ac97->rates_adc); } else { ac97->rates_front_dac = SNDRV_PCM_RATE_48000; ac97->rates_adc = SNDRV_PCM_RATE_48000; } if (ac97->ext_id & 0x0008) { /* MIC VRA support */ snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, &ac97->rates_mic_adc); } else { ac97->rates_mic_adc = SNDRV_PCM_RATE_48000; } if (ac97->ext_id & 0x0080) { /* SDAC support */ snd_ac97_determine_rates(ac97, AC97_PCM_SURR_DAC_RATE, &ac97->rates_surr_dac); ac97->scaps |= AC97_SCAP_SURROUND_DAC; } if (ac97->ext_id & 0x0100) { /* LDAC support */ snd_ac97_determine_rates(ac97, AC97_PCM_LFE_DAC_RATE, &ac97->rates_lfe_dac); ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC; } if (ac97->init) ac97->init(ac97); snd_ac97_get_name(ac97, ac97->id, name); snd_ac97_get_name(NULL, ac97->id, name); // ac97->id might be changed in the special setup code if (card->mixername[0] == '\0') { strcpy(card->mixername, name); } else { if (strlen(card->mixername) + 1 + strlen(name) + 1 <= sizeof(card->mixername)) { strcat(card->mixername, ","); strcat(card->mixername, name); } } if ((err = snd_component_add(card, "AC97")) < 0) { snd_ac97_free(ac97); return err; } if (snd_ac97_mixer_build(card, ac97) < 0) { snd_ac97_free(ac97); return -ENOMEM; } snd_ac97_proc_init(card, ac97); if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ac97, &ops)) < 0) { snd_ac97_free(ac97); return err; } *rac97 = ac97; return 0;}/* */static void snd_ac97_proc_read_main(ac97_t *ac97, snd_info_buffer_t * buffer, int subidx){ char name[64]; unsigned int id; unsigned short val, tmp, ext; static const char *spdif_slots[4] = { " SPDIF=3/4", " SPDIF=7/8", " SPDIF=6/9", " SPDIF=res" }; static const char *spdif_rates[4] = { " Rate=44.1kHz", " Rate=res", " Rate=48kHz", " Rate=32kHz" }; id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16; id |= snd_ac97_read(ac97, AC97_VENDOR_ID2); snd_ac97_get_name(NULL, id, name); snd_iprintf(buffer, "%d-%d/%d: %s\n\n", ac97->addr, ac97->num, subidx, name); val = snd_ac97_read(ac97, AC97_RESET); snd_iprintf(buffer, "Capabilities :%s%s%s%s%s%s\n", val & 0x0001 ? " -dedicated MIC PCM IN channel-" : "", val & 0x0002 ? " -reserved1-" : "", val & 0x0004 ? " -bass & treble-" : "", val & 0x0008 ? " -simulated stereo-" : "", val & 0x0010 ? " -headphone out-" : "", val & 0x0020 ? " -loudness-" : ""); tmp = ac97->caps & 0x00c0; snd_iprintf(buffer, "DAC resolution : %s%s%s%s\n", tmp == 0x0000 ? "16-bit" : "", tmp == 0x0040 ? "18-bit" : "", tmp == 0x0080 ? "20-bit" : "", tmp == 0x00c0 ? "???" : ""); tmp = ac97->caps & 0x0300; snd_iprintf(buffer, "ADC resolution : %s%s%s%s\n", tmp == 0x0000 ? "16-bit" : "", tmp == 0x0100 ? "18-bit" : "", tmp == 0x0200 ? "20-bit" : "", tmp == 0x0300 ? "???" : ""); snd_iprintf(buffer, "3D enhancement : %s\n", snd_ac97_stereo_enhancements[(val >> 10) & 0x1f]); snd_iprintf(buffer, "\nCurrent setup\n"); val = snd_ac97_read(ac97, AC97_MIC); snd_iprintf(buffer, "Mic gain : %s [%s]\n", val & 0x0040 ? "+20dB" : "+0dB", ac97->regs[AC97_MIC] & 0x0040 ? "+20dB" : "+0dB"); val = snd_ac97_read(ac97, AC97_GENERAL_PURPOSE); snd_iprintf(buffer, "POP path : %s 3D\n" "Sim. stereo : %s\n" "3D enhancement : %s\n" "Loudness : %s\n" "Mono output : %s\n" "Mic select : %s\n" "ADC/DAC loopback : %s\n", val & 0x8000 ? "post" : "pre",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -