📄 fm801.c
字号:
udelay(1); val <<= 1; if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_256PCS_DATA)) val |= 1; } spin_unlock_irq(&chip->reg_lock); return val;}/* 256PCPR GPIO numbers */#define TEA_256PCPR_BUS_CLOCK 0#define TEA_256PCPR_DATA 1#define TEA_256PCPR_WRITE_ENABLE 2 /* inverted */static void snd_fm801_tea575x_256pcpr_write(struct snd_tea575x *tea, unsigned int val){ struct fm801 *chip = tea->private_data; unsigned short reg; int i = 25; spin_lock_irq(&chip->reg_lock); reg = inw(FM801_REG(chip, GPIO_CTRL)); /* use GPIO lines and set write enable bit */ reg |= FM801_GPIO_GS(TEA_256PCPR_DATA) | FM801_GPIO_GS(TEA_256PCPR_WRITE_ENABLE) | FM801_GPIO_GS(TEA_256PCPR_BUS_CLOCK); /* all of lines are in the write direction */ /* clear data and clock lines */ reg &= ~(FM801_GPIO_GD(TEA_256PCPR_DATA) | FM801_GPIO_GD(TEA_256PCPR_WRITE_ENABLE) | FM801_GPIO_GD(TEA_256PCPR_BUS_CLOCK) | FM801_GPIO_GP(TEA_256PCPR_DATA) | FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK) | FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE)); outw(reg, FM801_REG(chip, GPIO_CTRL)); udelay(1); while (i--) { if (val & (1 << i)) reg |= FM801_GPIO_GP(TEA_256PCPR_DATA); else reg &= ~FM801_GPIO_GP(TEA_256PCPR_DATA); outw(reg, FM801_REG(chip, GPIO_CTRL)); udelay(1); reg |= FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK); outw(reg, FM801_REG(chip, GPIO_CTRL)); reg &= ~FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK); outw(reg, FM801_REG(chip, GPIO_CTRL)); udelay(1); } /* and reset the write enable bit */ reg |= FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE) | FM801_GPIO_GP(TEA_256PCPR_DATA); outw(reg, FM801_REG(chip, GPIO_CTRL)); spin_unlock_irq(&chip->reg_lock);}static unsigned int snd_fm801_tea575x_256pcpr_read(struct snd_tea575x *tea){ struct fm801 *chip = tea->private_data; unsigned short reg; unsigned int val = 0; int i; spin_lock_irq(&chip->reg_lock); reg = inw(FM801_REG(chip, GPIO_CTRL)); /* use GPIO lines, set data direction to input */ reg |= FM801_GPIO_GS(TEA_256PCPR_DATA) | FM801_GPIO_GS(TEA_256PCPR_WRITE_ENABLE) | FM801_GPIO_GS(TEA_256PCPR_BUS_CLOCK) | FM801_GPIO_GD(TEA_256PCPR_DATA) | FM801_GPIO_GP(TEA_256PCPR_DATA) | FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE); /* all of lines are in the write direction, except data */ /* clear data, write enable and clock lines */ reg &= ~(FM801_GPIO_GD(TEA_256PCPR_WRITE_ENABLE) | FM801_GPIO_GD(TEA_256PCPR_BUS_CLOCK) | FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK)); for (i = 0; i < 24; i++) { reg &= ~FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK); outw(reg, FM801_REG(chip, GPIO_CTRL)); udelay(1); reg |= FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK); outw(reg, FM801_REG(chip, GPIO_CTRL)); udelay(1); val <<= 1; if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_256PCPR_DATA)) val |= 1; } spin_unlock_irq(&chip->reg_lock); return val;}/* 64PCR GPIO numbers */#define TEA_64PCR_BUS_CLOCK 0#define TEA_64PCR_WRITE_ENABLE 1 /* inverted */#define TEA_64PCR_DATA 2static void snd_fm801_tea575x_64pcr_write(struct snd_tea575x *tea, unsigned int val){ struct fm801 *chip = tea->private_data; unsigned short reg; int i = 25; spin_lock_irq(&chip->reg_lock); reg = inw(FM801_REG(chip, GPIO_CTRL)); /* use GPIO lines and set write enable bit */ reg |= FM801_GPIO_GS(TEA_64PCR_DATA) | FM801_GPIO_GS(TEA_64PCR_WRITE_ENABLE) | FM801_GPIO_GS(TEA_64PCR_BUS_CLOCK); /* all of lines are in the write direction */ /* clear data and clock lines */ reg &= ~(FM801_GPIO_GD(TEA_64PCR_DATA) | FM801_GPIO_GD(TEA_64PCR_WRITE_ENABLE) | FM801_GPIO_GD(TEA_64PCR_BUS_CLOCK) | FM801_GPIO_GP(TEA_64PCR_DATA) | FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK) | FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE)); outw(reg, FM801_REG(chip, GPIO_CTRL)); udelay(1); while (i--) { if (val & (1 << i)) reg |= FM801_GPIO_GP(TEA_64PCR_DATA); else reg &= ~FM801_GPIO_GP(TEA_64PCR_DATA); outw(reg, FM801_REG(chip, GPIO_CTRL)); udelay(1); reg |= FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK); outw(reg, FM801_REG(chip, GPIO_CTRL)); reg &= ~FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK); outw(reg, FM801_REG(chip, GPIO_CTRL)); udelay(1); } /* and reset the write enable bit */ reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE) | FM801_GPIO_GP(TEA_64PCR_DATA); outw(reg, FM801_REG(chip, GPIO_CTRL)); spin_unlock_irq(&chip->reg_lock);}static unsigned int snd_fm801_tea575x_64pcr_read(struct snd_tea575x *tea){ struct fm801 *chip = tea->private_data; unsigned short reg; unsigned int val = 0; int i; spin_lock_irq(&chip->reg_lock); reg = inw(FM801_REG(chip, GPIO_CTRL)); /* use GPIO lines, set data direction to input */ reg |= FM801_GPIO_GS(TEA_64PCR_DATA) | FM801_GPIO_GS(TEA_64PCR_WRITE_ENABLE) | FM801_GPIO_GS(TEA_64PCR_BUS_CLOCK) | FM801_GPIO_GD(TEA_64PCR_DATA) | FM801_GPIO_GP(TEA_64PCR_DATA) | FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE); /* all of lines are in the write direction, except data */ /* clear data, write enable and clock lines */ reg &= ~(FM801_GPIO_GD(TEA_64PCR_WRITE_ENABLE) | FM801_GPIO_GD(TEA_64PCR_BUS_CLOCK) | FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK)); for (i = 0; i < 24; i++) { reg &= ~FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK); outw(reg, FM801_REG(chip, GPIO_CTRL)); udelay(1); reg |= FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK); outw(reg, FM801_REG(chip, GPIO_CTRL)); udelay(1); val <<= 1; if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_64PCR_DATA)) val |= 1; } spin_unlock_irq(&chip->reg_lock); return val;}static struct snd_tea575x_ops snd_fm801_tea_ops[3] = { { /* 1 = MediaForte 256-PCS */ .write = snd_fm801_tea575x_256pcs_write, .read = snd_fm801_tea575x_256pcs_read, }, { /* 2 = MediaForte 256-PCPR */ .write = snd_fm801_tea575x_256pcpr_write, .read = snd_fm801_tea575x_256pcpr_read, }, { /* 3 = MediaForte 64-PCR */ .write = snd_fm801_tea575x_64pcr_write, .read = snd_fm801_tea575x_64pcr_read, }};#endif/* * Mixer routines */#define FM801_SINGLE(xname, reg, shift, mask, invert) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_fm801_info_single, \ .get = snd_fm801_get_single, .put = snd_fm801_put_single, \ .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }static int snd_fm801_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo){ int mask = (kcontrol->private_value >> 16) & 0xff; uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = mask; return 0;}static int snd_fm801_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct fm801 *chip = snd_kcontrol_chip(kcontrol); int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0xff; int mask = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; ucontrol->value.integer.value[0] = (inw(chip->port + reg) >> shift) & mask; if (invert) ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; return 0;}static int snd_fm801_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct fm801 *chip = snd_kcontrol_chip(kcontrol); int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0xff; int mask = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; unsigned short val; val = (ucontrol->value.integer.value[0] & mask); if (invert) val = mask - val; return snd_fm801_update_bits(chip, reg, mask << shift, val << shift);}#define FM801_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_fm801_info_double, \ .get = snd_fm801_get_double, .put = snd_fm801_put_double, \ .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) }#define FM801_DOUBLE_TLV(xname, reg, shift_left, shift_right, mask, invert, xtlv) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ .name = xname, .info = snd_fm801_info_double, \ .get = snd_fm801_get_double, .put = snd_fm801_put_double, \ .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24), \ .tlv = { .p = (xtlv) } }static int snd_fm801_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo){ int mask = (kcontrol->private_value >> 16) & 0xff; uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; uinfo->value.integer.min = 0; uinfo->value.integer.max = mask; return 0;}static int snd_fm801_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct fm801 *chip = snd_kcontrol_chip(kcontrol); int reg = kcontrol->private_value & 0xff; int shift_left = (kcontrol->private_value >> 8) & 0x0f; int shift_right = (kcontrol->private_value >> 12) & 0x0f; int mask = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; spin_lock_irq(&chip->reg_lock); ucontrol->value.integer.value[0] = (inw(chip->port + reg) >> shift_left) & mask; ucontrol->value.integer.value[1] = (inw(chip->port + reg) >> shift_right) & mask; spin_unlock_irq(&chip->reg_lock); if (invert) { ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; } return 0;}static int snd_fm801_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct fm801 *chip = snd_kcontrol_chip(kcontrol); int reg = kcontrol->private_value & 0xff; int shift_left = (kcontrol->private_value >> 8) & 0x0f; int shift_right = (kcontrol->private_value >> 12) & 0x0f; int mask = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; unsigned short val1, val2; val1 = ucontrol->value.integer.value[0] & mask; val2 = ucontrol->value.integer.value[1] & mask; if (invert) { val1 = mask - val1; val2 = mask - val2; } return snd_fm801_update_bits(chip, reg, (mask << shift_left) | (mask << shift_right), (val1 << shift_left ) | (val2 << shift_right));}static int snd_fm801_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo){ static char *texts[5] = { "AC97 Primary", "FM", "I2S", "PCM", "AC97 Secondary" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = 5; if (uinfo->value.enumerated.item > 4) uinfo->value.enumerated.item = 4; strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); return 0;}static int snd_fm801_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct fm801 *chip = snd_kcontrol_chip(kcontrol); unsigned short val; val = inw(FM801_REG(chip, REC_SRC)) & 7; if (val > 4) val = 4; ucontrol->value.enumerated.item[0] = val; return 0;}static int snd_fm801_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct fm801 *chip = snd_kcontrol_chip(kcontrol); unsigned short val; if ((val = ucontrol->value.enumerated.item[0]) > 4) return -EINVAL; return snd_fm801_update_bits(chip, FM801_REC_SRC, 7, val);}static const DECLARE_TLV_DB_SCALE(db_scale_dsp, -3450, 150, 0);#define FM801_CONTROLS ARRAY_SIZE(snd_fm801_controls)static struct snd_kcontrol_new snd_fm801_controls[] __devinitdata = {FM801_DOUBLE_TLV("Wave Playback Volume", FM801_PCM_VOL, 0, 8, 31, 1, db_scale_dsp),FM801_SINGLE("Wave Playback Switch", FM801_PCM_VOL, 15, 1, 1),FM801_DOUBLE_TLV("I2S Playback Volume", FM801_I2S_VOL, 0, 8, 31, 1, db_scale_dsp),FM801_SINGLE("I2S Playback Switch", FM801_I2S_VOL, 15, 1, 1),FM801_DOUBLE_TLV("FM Playback Volume", FM801_FM_VOL, 0, 8, 31, 1, db_scale_dsp),FM801_SINGLE("FM Playback Switch", FM801_FM_VOL, 15, 1, 1),{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Digital Capture Source", .info = snd_fm801_info_mux, .get = snd_fm801_get_mux, .put = snd_fm801_put_mux,}};#define FM801_CONTROLS_MULTI ARRAY_SIZE(snd_fm801_controls_multi)static struct snd_kcontrol_new snd_fm801_controls_multi[] __devinitdata = {FM801_SINGLE("AC97 2ch->4ch Copy Switch", FM801_CODEC_CTRL, 7, 1, 0),FM801_SINGLE("AC97 18-bit Switch", FM801_CODEC_CTRL, 10, 1, 0),FM801_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), FM801_I2S_MODE, 8, 1, 0),FM801_SINGLE(SNDRV_CTL_NAME_IEC958("Raw Data ",PLAYBACK,SWITCH), FM801_I2S_MODE, 9, 1, 0),FM801_SINGLE(SNDRV_CTL_NAME_IEC958("Raw Data ",CAPTURE,SWITCH), FM801_I2S_MODE, 10, 1, 0),FM801_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), FM801_GEN_CTRL, 2, 1, 0),
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -