📄 awacs.c
字号:
/* * PMac AWACS lowlevel functions * * Copyright (c) by Takashi Iwai <tiwai@suse.de> * code based on dmasound.c. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <sound/driver.h>#include <asm/io.h>#include <asm/nvram.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/slab.h>#include <sound/core.h>#include "pmac.h"#ifdef CONFIG_ADB_CUDA#define PMAC_AMP_AVAIL#endif#ifdef PMAC_AMP_AVAILstruct awacs_amp { unsigned char amp_master; unsigned char amp_vol[2][2]; unsigned char amp_tone[2];};#define CHECK_CUDA_AMP() (sys_ctrler == SYS_CTRLER_CUDA)#endif /* PMAC_AMP_AVAIL */static void snd_pmac_screamer_wait(struct snd_pmac *chip){ long timeout = 2000; while (!(in_le32(&chip->awacs->codec_stat) & MASK_VALID)) { mdelay(1); if (! --timeout) { snd_printd("snd_pmac_screamer_wait timeout\n"); break; } }}/* * write AWACS register */static voidsnd_pmac_awacs_write(struct snd_pmac *chip, int val){ long timeout = 5000000; if (chip->model == PMAC_SCREAMER) snd_pmac_screamer_wait(chip); out_le32(&chip->awacs->codec_ctrl, val | (chip->subframe << 22)); while (in_le32(&chip->awacs->codec_ctrl) & MASK_NEWECMD) { if (! --timeout) { snd_printd("snd_pmac_awacs_write timeout\n"); break; } }}static voidsnd_pmac_awacs_write_reg(struct snd_pmac *chip, int reg, int val){ snd_pmac_awacs_write(chip, val | (reg << 12)); chip->awacs_reg[reg] = val;}static voidsnd_pmac_awacs_write_noreg(struct snd_pmac *chip, int reg, int val){ snd_pmac_awacs_write(chip, val | (reg << 12));}#ifdef CONFIG_PM/* Recalibrate chip */static void screamer_recalibrate(struct snd_pmac *chip){ if (chip->model != PMAC_SCREAMER) return; /* Sorry for the horrible delays... I hope to get that improved * by making the whole PM process asynchronous in a future version */ snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]); if (chip->manufacturer == 0x1) /* delay for broken crystal part */ msleep(750); snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1] | MASK_RECALIBRATE | MASK_CMUTE | MASK_AMUTE); snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]); snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]);}#else#define screamer_recalibrate(chip) /* NOP */#endif/* * additional callback to set the pcm format */static void snd_pmac_awacs_set_format(struct snd_pmac *chip){ chip->awacs_reg[1] &= ~MASK_SAMPLERATE; chip->awacs_reg[1] |= chip->rate_index << 3; snd_pmac_awacs_write_reg(chip, 1, chip->awacs_reg[1]);}/* * AWACS volume callbacks *//* * volumes: 0-15 stereo */static int snd_pmac_awacs_info_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo){ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; uinfo->value.integer.min = 0; uinfo->value.integer.max = 15; return 0;} static int snd_pmac_awacs_get_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); int reg = kcontrol->private_value & 0xff; int lshift = (kcontrol->private_value >> 8) & 0xff; int inverted = (kcontrol->private_value >> 16) & 1; unsigned long flags; int vol[2]; spin_lock_irqsave(&chip->reg_lock, flags); vol[0] = (chip->awacs_reg[reg] >> lshift) & 0xf; vol[1] = chip->awacs_reg[reg] & 0xf; spin_unlock_irqrestore(&chip->reg_lock, flags); if (inverted) { vol[0] = 0x0f - vol[0]; vol[1] = 0x0f - vol[1]; } ucontrol->value.integer.value[0] = vol[0]; ucontrol->value.integer.value[1] = vol[1]; return 0;}static int snd_pmac_awacs_put_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); int reg = kcontrol->private_value & 0xff; int lshift = (kcontrol->private_value >> 8) & 0xff; int inverted = (kcontrol->private_value >> 16) & 1; int val, oldval; unsigned long flags; int vol[2]; vol[0] = ucontrol->value.integer.value[0]; vol[1] = ucontrol->value.integer.value[1]; if (inverted) { vol[0] = 0x0f - vol[0]; vol[1] = 0x0f - vol[1]; } vol[0] &= 0x0f; vol[1] &= 0x0f; spin_lock_irqsave(&chip->reg_lock, flags); oldval = chip->awacs_reg[reg]; val = oldval & ~(0xf | (0xf << lshift)); val |= vol[0] << lshift; val |= vol[1]; if (oldval != val) snd_pmac_awacs_write_reg(chip, reg, val); spin_unlock_irqrestore(&chip->reg_lock, flags); return oldval != reg;}#define AWACS_VOLUME(xname, xreg, xshift, xinverted) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ .info = snd_pmac_awacs_info_volume, \ .get = snd_pmac_awacs_get_volume, \ .put = snd_pmac_awacs_put_volume, \ .private_value = (xreg) | ((xshift) << 8) | ((xinverted) << 16) }/* * mute master/ogain for AWACS: mono */static int snd_pmac_awacs_get_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0xff; int invert = (kcontrol->private_value >> 16) & 1; int val; unsigned long flags; spin_lock_irqsave(&chip->reg_lock, flags); val = (chip->awacs_reg[reg] >> shift) & 1; spin_unlock_irqrestore(&chip->reg_lock, flags); if (invert) val = 1 - val; ucontrol->value.integer.value[0] = val; return 0;}static int snd_pmac_awacs_put_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0xff; int invert = (kcontrol->private_value >> 16) & 1; int mask = 1 << shift; int val, changed; unsigned long flags; spin_lock_irqsave(&chip->reg_lock, flags); val = chip->awacs_reg[reg] & ~mask; if (ucontrol->value.integer.value[0] != invert) val |= mask; changed = chip->awacs_reg[reg] != val; if (changed) snd_pmac_awacs_write_reg(chip, reg, val); spin_unlock_irqrestore(&chip->reg_lock, flags); return changed;}#define AWACS_SWITCH(xname, xreg, xshift, xinvert) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ .info = snd_pmac_boolean_mono_info, \ .get = snd_pmac_awacs_get_switch, \ .put = snd_pmac_awacs_put_switch, \ .private_value = (xreg) | ((xshift) << 8) | ((xinvert) << 16) }#ifdef PMAC_AMP_AVAIL/* * controls for perch/whisper extension cards, e.g. G3 desktop * * TDA7433 connected via i2c address 0x45 (= 0x8a), * accessed through cuda */static void awacs_set_cuda(int reg, int val){ struct adb_request req; cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, 0x8a, reg, val); while (! req.complete) cuda_poll();}/* * level = 0 - 14, 7 = 0 dB */static void awacs_amp_set_tone(struct awacs_amp *amp, int bass, int treble){ amp->amp_tone[0] = bass; amp->amp_tone[1] = treble; if (bass > 7) bass = (14 - bass) + 8; if (treble > 7) treble = (14 - treble) + 8; awacs_set_cuda(2, (bass << 4) | treble);}/* * vol = 0 - 31 (attenuation), 32 = mute bit, stereo */static int awacs_amp_set_vol(struct awacs_amp *amp, int index, int lvol, int rvol, int do_check){ if (do_check && amp->amp_vol[index][0] == lvol && amp->amp_vol[index][1] == rvol) return 0; awacs_set_cuda(3 + index, lvol); awacs_set_cuda(5 + index, rvol); amp->amp_vol[index][0] = lvol; amp->amp_vol[index][1] = rvol; return 1;}/* * 0 = -79 dB, 79 = 0 dB, 99 = +20 dB */static void awacs_amp_set_master(struct awacs_amp *amp, int vol){ amp->amp_master = vol; if (vol <= 79) vol = 32 + (79 - vol); else vol = 32 - (vol - 79); awacs_set_cuda(1, vol);}static void awacs_amp_free(struct snd_pmac *chip){ struct awacs_amp *amp = chip->mixer_data; snd_assert(amp, return); kfree(amp); chip->mixer_data = NULL; chip->mixer_free = NULL;}/* * mixer controls */static int snd_pmac_awacs_info_volume_amp(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo){ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; uinfo->value.integer.min = 0; uinfo->value.integer.max = 31; return 0;} static int snd_pmac_awacs_get_volume_amp(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); int index = kcontrol->private_value; struct awacs_amp *amp = chip->mixer_data; snd_assert(amp, return -EINVAL); snd_assert(index >= 0 && index <= 1, return -EINVAL); ucontrol->value.integer.value[0] = 31 - (amp->amp_vol[index][0] & 31); ucontrol->value.integer.value[1] = 31 - (amp->amp_vol[index][1] & 31); return 0;}static int snd_pmac_awacs_put_volume_amp(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); int index = kcontrol->private_value; int vol[2]; struct awacs_amp *amp = chip->mixer_data; snd_assert(amp, return -EINVAL); snd_assert(index >= 0 && index <= 1, return -EINVAL); vol[0] = (31 - (ucontrol->value.integer.value[0] & 31)) | (amp->amp_vol[index][0] & 32); vol[1] = (31 - (ucontrol->value.integer.value[1] & 31)) | (amp->amp_vol[index][1] & 32); return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1);}static int snd_pmac_awacs_get_switch_amp(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); int index = kcontrol->private_value; struct awacs_amp *amp = chip->mixer_data; snd_assert(amp, return -EINVAL); snd_assert(index >= 0 && index <= 1, return -EINVAL); ucontrol->value.integer.value[0] = (amp->amp_vol[index][0] & 32) ? 0 : 1; ucontrol->value.integer.value[1] = (amp->amp_vol[index][1] & 32) ? 0 : 1; return 0;}static int snd_pmac_awacs_put_switch_amp(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); int index = kcontrol->private_value; int vol[2]; struct awacs_amp *amp = chip->mixer_data; snd_assert(amp, return -EINVAL); snd_assert(index >= 0 && index <= 1, return -EINVAL); vol[0] = (ucontrol->value.integer.value[0] ? 0 : 32) | (amp->amp_vol[index][0] & 31); vol[1] = (ucontrol->value.integer.value[1] ? 0 : 32) | (amp->amp_vol[index][1] & 31); return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1);}static int snd_pmac_awacs_info_tone_amp(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 = 14; return 0;} static int snd_pmac_awacs_get_tone_amp(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); int index = kcontrol->private_value; struct awacs_amp *amp = chip->mixer_data; snd_assert(amp, return -EINVAL); snd_assert(index >= 0 && index <= 1, return -EINVAL); ucontrol->value.integer.value[0] = amp->amp_tone[index]; return 0;}static int snd_pmac_awacs_put_tone_amp(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); int index = kcontrol->private_value; struct awacs_amp *amp = chip->mixer_data; snd_assert(amp, return -EINVAL); snd_assert(index >= 0 && index <= 1, return -EINVAL); if (ucontrol->value.integer.value[0] != amp->amp_tone[index]) { amp->amp_tone[index] = ucontrol->value.integer.value[0]; awacs_amp_set_tone(amp, amp->amp_tone[0], amp->amp_tone[1]); return 1; } return 0;}static int snd_pmac_awacs_info_master_amp(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 = 99; return 0;} static int snd_pmac_awacs_get_master_amp(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); struct awacs_amp *amp = chip->mixer_data; snd_assert(amp, return -EINVAL); ucontrol->value.integer.value[0] = amp->amp_master; return 0;}static int snd_pmac_awacs_put_master_amp(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); struct awacs_amp *amp = chip->mixer_data; snd_assert(amp, return -EINVAL); if (ucontrol->value.integer.value[0] != amp->amp_master) { amp->amp_master = ucontrol->value.integer.value[0]; awacs_amp_set_master(amp, amp->amp_master);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -