📄 awacs.c
字号:
return 1; } return 0;}#define AMP_CH_SPK 0#define AMP_CH_HD 1static struct snd_kcontrol_new snd_pmac_awacs_amp_vol[] __initdata = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PC Speaker Playback Volume", .info = snd_pmac_awacs_info_volume_amp, .get = snd_pmac_awacs_get_volume_amp, .put = snd_pmac_awacs_put_volume_amp, .private_value = AMP_CH_SPK, }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Headphone Playback Volume", .info = snd_pmac_awacs_info_volume_amp, .get = snd_pmac_awacs_get_volume_amp, .put = snd_pmac_awacs_put_volume_amp, .private_value = AMP_CH_HD, }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Tone Control - Bass", .info = snd_pmac_awacs_info_tone_amp, .get = snd_pmac_awacs_get_tone_amp, .put = snd_pmac_awacs_put_tone_amp, .private_value = 0, }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Tone Control - Treble", .info = snd_pmac_awacs_info_tone_amp, .get = snd_pmac_awacs_get_tone_amp, .put = snd_pmac_awacs_put_tone_amp, .private_value = 1, }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Amp Master Playback Volume", .info = snd_pmac_awacs_info_master_amp, .get = snd_pmac_awacs_get_master_amp, .put = snd_pmac_awacs_put_master_amp, },};static struct snd_kcontrol_new snd_pmac_awacs_amp_hp_sw __initdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Headphone Playback Switch", .info = snd_pmac_boolean_stereo_info, .get = snd_pmac_awacs_get_switch_amp, .put = snd_pmac_awacs_put_switch_amp, .private_value = AMP_CH_HD,};static struct snd_kcontrol_new snd_pmac_awacs_amp_spk_sw __initdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PC Speaker Playback Switch", .info = snd_pmac_boolean_stereo_info, .get = snd_pmac_awacs_get_switch_amp, .put = snd_pmac_awacs_put_switch_amp, .private_value = AMP_CH_SPK,};#endif /* PMAC_AMP_AVAIL *//* * mic boost for screamer */static int snd_pmac_screamer_mic_boost_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo){ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = 2; return 0;}static int snd_pmac_screamer_mic_boost_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); int val; unsigned long flags; spin_lock_irqsave(&chip->reg_lock, flags); if (chip->awacs_reg[6] & MASK_MIC_BOOST) val = 2; else if (chip->awacs_reg[0] & MASK_GAINLINE) val = 1; else val = 0; spin_unlock_irqrestore(&chip->reg_lock, flags); ucontrol->value.integer.value[0] = val; return 0;}static int snd_pmac_screamer_mic_boost_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); int changed = 0; int val0, val6; unsigned long flags; spin_lock_irqsave(&chip->reg_lock, flags); val0 = chip->awacs_reg[0] & ~MASK_GAINLINE; val6 = chip->awacs_reg[6] & ~MASK_MIC_BOOST; if (ucontrol->value.integer.value[0] > 0) { val0 |= MASK_GAINLINE; if (ucontrol->value.integer.value[0] > 1) val6 |= MASK_MIC_BOOST; } if (val0 != chip->awacs_reg[0]) { snd_pmac_awacs_write_reg(chip, 0, val0); changed = 1; } if (val6 != chip->awacs_reg[6]) { snd_pmac_awacs_write_reg(chip, 6, val6); changed = 1; } spin_unlock_irqrestore(&chip->reg_lock, flags); return changed;}/* * lists of mixer elements */static struct snd_kcontrol_new snd_pmac_awacs_mixers[] __initdata = { AWACS_VOLUME("Master Playback Volume", 2, 6, 1), AWACS_SWITCH("Master Capture Switch", 1, SHIFT_LOOPTHRU, 0), AWACS_VOLUME("Capture Volume", 0, 4, 0), AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),};/* FIXME: is this correct order? * screamer (powerbook G3 pismo) seems to have different bits... */static struct snd_kcontrol_new snd_pmac_awacs_mixers2[] __initdata = { AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_LINE, 0), AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_MIC, 0),};static struct snd_kcontrol_new snd_pmac_screamer_mixers2[] __initdata = { AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0), AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_LINE, 0),};static struct snd_kcontrol_new snd_pmac_awacs_master_sw __initdata =AWACS_SWITCH("Master Playback Switch", 1, SHIFT_HDMUTE, 1);static struct snd_kcontrol_new snd_pmac_awacs_mic_boost[] __initdata = { AWACS_SWITCH("Mic Boost", 0, SHIFT_GAINLINE, 0),};static struct snd_kcontrol_new snd_pmac_screamer_mic_boost[] __initdata = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Mic Boost", .info = snd_pmac_screamer_mic_boost_info, .get = snd_pmac_screamer_mic_boost_get, .put = snd_pmac_screamer_mic_boost_put, },};static struct snd_kcontrol_new snd_pmac_awacs_speaker_vol[] __initdata = { AWACS_VOLUME("PC Speaker Playback Volume", 4, 6, 1),};static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw __initdata =AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_SPKMUTE, 1);/* * add new mixer elements to the card */static int build_mixers(struct snd_pmac *chip, int nums, struct snd_kcontrol_new *mixers){ int i, err; for (i = 0; i < nums; i++) { if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixers[i], chip))) < 0) return err; } return 0;}/* * restore all registers */static void awacs_restore_all_regs(struct snd_pmac *chip){ snd_pmac_awacs_write_noreg(chip, 0, chip->awacs_reg[0]); snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]); snd_pmac_awacs_write_noreg(chip, 2, chip->awacs_reg[2]); snd_pmac_awacs_write_noreg(chip, 4, chip->awacs_reg[4]); if (chip->model == PMAC_SCREAMER) { snd_pmac_awacs_write_noreg(chip, 5, chip->awacs_reg[5]); snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]); snd_pmac_awacs_write_noreg(chip, 7, chip->awacs_reg[7]); }}#ifdef CONFIG_PMstatic void snd_pmac_awacs_suspend(struct snd_pmac *chip){ snd_pmac_awacs_write_noreg(chip, 1, (chip->awacs_reg[1] | MASK_AMUTE | MASK_CMUTE));}static void snd_pmac_awacs_resume(struct snd_pmac *chip){ if (machine_is_compatible("PowerBook3,1") || machine_is_compatible("PowerBook3,2")) { msleep(100); snd_pmac_awacs_write_reg(chip, 1, chip->awacs_reg[1] & ~MASK_PAROUT); msleep(300); } awacs_restore_all_regs(chip); if (chip->model == PMAC_SCREAMER) { /* reset power bits in reg 6 */ mdelay(5); snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]); } screamer_recalibrate(chip);#ifdef PMAC_AMP_AVAIL if (chip->mixer_data) { struct awacs_amp *amp = chip->mixer_data; awacs_amp_set_vol(amp, 0, amp->amp_vol[0][0], amp->amp_vol[0][1], 0); awacs_amp_set_vol(amp, 1, amp->amp_vol[1][0], amp->amp_vol[1][1], 0); awacs_amp_set_tone(amp, amp->amp_tone[0], amp->amp_tone[1]); awacs_amp_set_master(amp, amp->amp_master); }#endif}#endif /* CONFIG_PM */#ifdef PMAC_SUPPORT_AUTOMUTE/* * auto-mute stuffs */static int snd_pmac_awacs_detect_headphone(struct snd_pmac *chip){ return (in_le32(&chip->awacs->codec_stat) & chip->hp_stat_mask) ? 1 : 0;}#ifdef PMAC_AMP_AVAILstatic int toggle_amp_mute(struct awacs_amp *amp, int index, int mute){ int vol[2]; vol[0] = amp->amp_vol[index][0] & 31; vol[1] = amp->amp_vol[index][1] & 31; if (mute) { vol[0] |= 32; vol[1] |= 32; } return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1);}#endifstatic void snd_pmac_awacs_update_automute(struct snd_pmac *chip, int do_notify){ if (chip->auto_mute) {#ifdef PMAC_AMP_AVAIL if (chip->mixer_data) { struct awacs_amp *amp = chip->mixer_data; int changed; if (snd_pmac_awacs_detect_headphone(chip)) { changed = toggle_amp_mute(amp, AMP_CH_HD, 0); changed |= toggle_amp_mute(amp, AMP_CH_SPK, 1); } else { changed = toggle_amp_mute(amp, AMP_CH_HD, 1); changed |= toggle_amp_mute(amp, AMP_CH_SPK, 0); } if (do_notify && ! changed) return; } else#endif { int reg = chip->awacs_reg[1] | (MASK_HDMUTE|MASK_SPKMUTE); if (snd_pmac_awacs_detect_headphone(chip)) reg &= ~MASK_HDMUTE; else reg &= ~MASK_SPKMUTE; if (do_notify && reg == chip->awacs_reg[1]) return; snd_pmac_awacs_write_reg(chip, 1, reg); } if (do_notify) { snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_sw_ctl->id); snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->speaker_sw_ctl->id); snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hp_detect_ctl->id); } }}#endif /* PMAC_SUPPORT_AUTOMUTE *//* * initialize chip */int __initsnd_pmac_awacs_init(struct snd_pmac *chip){ int err, vol; /* looks like MASK_GAINLINE triggers something, so we set here * as start-up */ chip->awacs_reg[0] = MASK_MUX_CD | 0xff | MASK_GAINLINE; chip->awacs_reg[1] = MASK_CMUTE | MASK_AMUTE; /* FIXME: Only machines with external SRS module need MASK_PAROUT */ if (chip->has_iic || chip->device_id == 0x5 || /*chip->_device_id == 0x8 || */ chip->device_id == 0xb) chip->awacs_reg[1] |= MASK_PAROUT; /* get default volume from nvram */ // vol = (~nvram_read_byte(0x1308) & 7) << 1; // vol = ((pmac_xpram_read( 8 ) & 7 ) << 1 ); vol = 0x0f; /* no, on alsa, muted as default */ vol = vol + (vol << 6); chip->awacs_reg[2] = vol; chip->awacs_reg[4] = vol; if (chip->model == PMAC_SCREAMER) { chip->awacs_reg[5] = vol; /* FIXME: screamer has loopthru vol control */ chip->awacs_reg[6] = MASK_MIC_BOOST; /* FIXME: maybe should be vol << 3 for PCMCIA speaker */ chip->awacs_reg[7] = 0; } awacs_restore_all_regs(chip); chip->manufacturer = (in_le32(&chip->awacs->codec_stat) >> 8) & 0xf; screamer_recalibrate(chip); chip->revision = (in_le32(&chip->awacs->codec_stat) >> 12) & 0xf;#ifdef PMAC_AMP_AVAIL if (chip->revision == 3 && chip->has_iic && CHECK_CUDA_AMP()) { struct awacs_amp *amp = kzalloc(sizeof(*amp), GFP_KERNEL); if (! amp) return -ENOMEM; chip->mixer_data = amp; chip->mixer_free = awacs_amp_free; awacs_amp_set_vol(amp, 0, 63, 63, 0); /* mute and zero vol */ awacs_amp_set_vol(amp, 1, 63, 63, 0); awacs_amp_set_tone(amp, 7, 7); /* 0 dB */ awacs_amp_set_master(amp, 79); /* 0 dB */ }#endif /* PMAC_AMP_AVAIL */ if (chip->hp_stat_mask == 0) { /* set headphone-jack detection bit */ switch (chip->model) { case PMAC_AWACS: chip->hp_stat_mask = 0x04; break; case PMAC_SCREAMER: switch (chip->device_id) { case 0x08: /* 1 = side jack, 2 = front jack */ chip->hp_stat_mask = 0x03; break; case 0x00: case 0x05: chip->hp_stat_mask = 0x04; break; default: chip->hp_stat_mask = 0x08; break; } break; default: snd_BUG(); break; } } /* * build mixers */ strcpy(chip->card->mixername, "PowerMac AWACS"); if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers), snd_pmac_awacs_mixers)) < 0) return err; if (chip->model == PMAC_SCREAMER) err = build_mixers(chip, ARRAY_SIZE(snd_pmac_screamer_mixers2), snd_pmac_screamer_mixers2); else err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers2), snd_pmac_awacs_mixers2); if (err < 0) return err; chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_master_sw, chip); if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0) return err;#ifdef PMAC_AMP_AVAIL if (chip->mixer_data) { /* use amplifier. the signal is connected from route A * to the amp. the amp has its headphone and speaker * volumes and mute switches, so we use them instead of * screamer registers. * in this case, it seems the route C is not used. */ if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_amp_vol), snd_pmac_awacs_amp_vol)) < 0) return err; /* overwrite */ chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_hp_sw, chip); if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0) return err; chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_spk_sw, chip); if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0) return err; } else#endif /* PMAC_AMP_AVAIL */ { /* route A = headphone, route C = speaker */ if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_speaker_vol), snd_pmac_awacs_speaker_vol)) < 0) return err; chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_speaker_sw, chip); if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0) return err; } if (chip->model == PMAC_SCREAMER) { if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_screamer_mic_boost), snd_pmac_screamer_mic_boost)) < 0) return err; } else { if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mic_boost), snd_pmac_awacs_mic_boost)) < 0) return err; } /* * set lowlevel callbacks */ chip->set_format = snd_pmac_awacs_set_format;#ifdef CONFIG_PM chip->suspend = snd_pmac_awacs_suspend; chip->resume = snd_pmac_awacs_resume;#endif#ifdef PMAC_SUPPORT_AUTOMUTE if ((err = snd_pmac_add_automute(chip)) < 0) return err; chip->detect_headphone = snd_pmac_awacs_detect_headphone; chip->update_automute = snd_pmac_awacs_update_automute; snd_pmac_awacs_update_automute(chip, 0); /* update the status only */#endif if (chip->model == PMAC_SCREAMER) { snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]); snd_pmac_awacs_write_noreg(chip, 0, chip->awacs_reg[0]); } return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -