📄 azt3328.c
字号:
r->reg = val & 0xff; r->lchan_shift = (val >> 8) & 0x0f; r->rchan_shift = (val >> 12) & 0x0f; r->mask = (val >> 16) & 0xff; r->invert = (val >> 24) & 1; r->stereo = (val >> 25) & 1; r->enum_c = (val >> 26) & 0x0f;}/* * mixer switches/volumes */#define AZF3328_MIXER_SWITCH(xname, reg, shift, invert) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_azf3328_info_mixer, \ .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \ .private_value = COMPOSE_MIXER_REG(reg, shift, 0, 0x1, invert, 0, 0), \}#define AZF3328_MIXER_VOL_STEREO(xname, reg, mask, invert) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_azf3328_info_mixer, \ .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \ .private_value = COMPOSE_MIXER_REG(reg, 8, 0, mask, invert, 1, 0), \}#define AZF3328_MIXER_VOL_MONO(xname, reg, mask, is_right_chan) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_azf3328_info_mixer, \ .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \ .private_value = COMPOSE_MIXER_REG(reg, is_right_chan ? 0 : 8, 0, mask, 1, 0, 0), \}#define AZF3328_MIXER_VOL_SPECIAL(xname, reg, mask, shift, invert) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_azf3328_info_mixer, \ .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \ .private_value = COMPOSE_MIXER_REG(reg, shift, 0, mask, invert, 0, 0), \}#define AZF3328_MIXER_ENUM(xname, reg, enum_c, shift) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_azf3328_info_mixer_enum, \ .get = snd_azf3328_get_mixer_enum, .put = snd_azf3328_put_mixer_enum, \ .private_value = COMPOSE_MIXER_REG(reg, shift, 0, 0, 0, 0, enum_c), \}static intsnd_azf3328_info_mixer(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo){ struct azf3328_mixer_reg reg; snd_azf3328_dbgcallenter(); snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); uinfo->type = reg.mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = reg.stereo + 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = reg.mask; snd_azf3328_dbgcallleave(); return 0;}static intsnd_azf3328_get_mixer(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol); struct azf3328_mixer_reg reg; unsigned int oreg, val; snd_azf3328_dbgcallenter(); snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); oreg = snd_azf3328_mixer_inw(chip, reg.reg); val = (oreg >> reg.lchan_shift) & reg.mask; if (reg.invert) val = reg.mask - val; ucontrol->value.integer.value[0] = val; if (reg.stereo) { val = (oreg >> reg.rchan_shift) & reg.mask; if (reg.invert) val = reg.mask - val; ucontrol->value.integer.value[1] = val; } snd_azf3328_dbgmixer("get: %02x is %04x -> vol %02lx|%02lx " "(shift %02d|%02d, mask %02x, inv. %d, stereo %d)\n", reg.reg, oreg, ucontrol->value.integer.value[0], ucontrol->value.integer.value[1], reg.lchan_shift, reg.rchan_shift, reg.mask, reg.invert, reg.stereo); snd_azf3328_dbgcallleave(); return 0;}static intsnd_azf3328_put_mixer(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol); struct azf3328_mixer_reg reg; unsigned int oreg, nreg, val; snd_azf3328_dbgcallenter(); snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); oreg = snd_azf3328_mixer_inw(chip, reg.reg); val = ucontrol->value.integer.value[0] & reg.mask; if (reg.invert) val = reg.mask - val; nreg = oreg & ~(reg.mask << reg.lchan_shift); nreg |= (val << reg.lchan_shift); if (reg.stereo) { val = ucontrol->value.integer.value[1] & reg.mask; if (reg.invert) val = reg.mask - val; nreg &= ~(reg.mask << reg.rchan_shift); nreg |= (val << reg.rchan_shift); } if (reg.mask >= 0x07) /* it's a volume control, so better take care */ snd_azf3328_mixer_write_volume_gradually( chip, reg.reg, nreg >> 8, nreg & 0xff, /* just set both channels, doesn't matter */ SET_CHAN_LEFT|SET_CHAN_RIGHT, 0); else snd_azf3328_mixer_outw(chip, reg.reg, nreg); snd_azf3328_dbgmixer("put: %02x to %02lx|%02lx, " "oreg %04x; shift %02d|%02d -> nreg %04x; after: %04x\n", reg.reg, ucontrol->value.integer.value[0], ucontrol->value.integer.value[1], oreg, reg.lchan_shift, reg.rchan_shift, nreg, snd_azf3328_mixer_inw(chip, reg.reg)); snd_azf3328_dbgcallleave(); return (nreg != oreg);}static intsnd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo){ static const char * const texts1[] = { "Mic1", "Mic2" }; static const char * const texts2[] = { "Mix", "Mic" }; static const char * const texts3[] = { "Mic", "CD", "Video", "Aux", "Line", "Mix", "Mix Mono", "Phone" }; static const char * const texts4[] = { "pre 3D", "post 3D" }; struct azf3328_mixer_reg reg; snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = (reg.reg == IDX_MIXER_REC_SELECT) ? 2 : 1; uinfo->value.enumerated.items = reg.enum_c; if (uinfo->value.enumerated.item > reg.enum_c - 1U) uinfo->value.enumerated.item = reg.enum_c - 1U; if (reg.reg == IDX_MIXER_ADVCTL2) { switch(reg.lchan_shift) { case 8: /* modem out sel */ strcpy(uinfo->value.enumerated.name, texts1[uinfo->value.enumerated.item]); break; case 9: /* mono sel source */ strcpy(uinfo->value.enumerated.name, texts2[uinfo->value.enumerated.item]); break; case 15: /* PCM Out Path */ strcpy(uinfo->value.enumerated.name, texts4[uinfo->value.enumerated.item]); break; } } else strcpy(uinfo->value.enumerated.name, texts3[uinfo->value.enumerated.item]); return 0;}static intsnd_azf3328_get_mixer_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol); struct azf3328_mixer_reg reg; unsigned short val; snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); val = snd_azf3328_mixer_inw(chip, reg.reg); if (reg.reg == IDX_MIXER_REC_SELECT) { ucontrol->value.enumerated.item[0] = (val >> 8) & (reg.enum_c - 1); ucontrol->value.enumerated.item[1] = (val >> 0) & (reg.enum_c - 1); } else ucontrol->value.enumerated.item[0] = (val >> reg.lchan_shift) & (reg.enum_c - 1); snd_azf3328_dbgmixer("get_enum: %02x is %04x -> %d|%d (shift %02d, enum_c %d)\n", reg.reg, val, ucontrol->value.enumerated.item[0], ucontrol->value.enumerated.item[1], reg.lchan_shift, reg.enum_c); return 0;}static intsnd_azf3328_put_mixer_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol); struct azf3328_mixer_reg reg; unsigned int oreg, nreg, val; snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); oreg = snd_azf3328_mixer_inw(chip, reg.reg); val = oreg; if (reg.reg == IDX_MIXER_REC_SELECT) { if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U || ucontrol->value.enumerated.item[1] > reg.enum_c - 1U) return -EINVAL; val = (ucontrol->value.enumerated.item[0] << 8) | (ucontrol->value.enumerated.item[1] << 0); } else { if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U) return -EINVAL; val &= ~((reg.enum_c - 1) << reg.lchan_shift); val |= (ucontrol->value.enumerated.item[0] << reg.lchan_shift); } snd_azf3328_mixer_outw(chip, reg.reg, val); nreg = val; snd_azf3328_dbgmixer("put_enum: %02x to %04x, oreg %04x\n", reg.reg, val, oreg); return (nreg != oreg);}static struct snd_kcontrol_new snd_azf3328_mixer_controls[] __devinitdata = { AZF3328_MIXER_SWITCH("Master Playback Switch", IDX_MIXER_PLAY_MASTER, 15, 1), AZF3328_MIXER_VOL_STEREO("Master Playback Volume", IDX_MIXER_PLAY_MASTER, 0x1f, 1), AZF3328_MIXER_SWITCH("Wave Playback Switch", IDX_MIXER_WAVEOUT, 15, 1), AZF3328_MIXER_VOL_STEREO("Wave Playback Volume", IDX_MIXER_WAVEOUT, 0x1f, 1), AZF3328_MIXER_SWITCH("Wave 3D Bypass Playback Switch", IDX_MIXER_ADVCTL2, 7, 1), AZF3328_MIXER_SWITCH("FM Playback Switch", IDX_MIXER_FMSYNTH, 15, 1), AZF3328_MIXER_VOL_STEREO("FM Playback Volume", IDX_MIXER_FMSYNTH, 0x1f, 1), AZF3328_MIXER_SWITCH("CD Playback Switch", IDX_MIXER_CDAUDIO, 15, 1), AZF3328_MIXER_VOL_STEREO("CD Playback Volume", IDX_MIXER_CDAUDIO, 0x1f, 1), AZF3328_MIXER_SWITCH("Capture Switch", IDX_MIXER_REC_VOLUME, 15, 1), AZF3328_MIXER_VOL_STEREO("Capture Volume", IDX_MIXER_REC_VOLUME, 0x0f, 0), AZF3328_MIXER_ENUM("Capture Source", IDX_MIXER_REC_SELECT, 8, 0), AZF3328_MIXER_SWITCH("Mic Playback Switch", IDX_MIXER_MIC, 15, 1), AZF3328_MIXER_VOL_MONO("Mic Playback Volume", IDX_MIXER_MIC, 0x1f, 1), AZF3328_MIXER_SWITCH("Mic Boost (+20dB)", IDX_MIXER_MIC, 6, 0), AZF3328_MIXER_SWITCH("Line Playback Switch", IDX_MIXER_LINEIN, 15, 1), AZF3328_MIXER_VOL_STEREO("Line Playback Volume", IDX_MIXER_LINEIN, 0x1f, 1), AZF3328_MIXER_SWITCH("PC Speaker Playback Switch", IDX_MIXER_PCBEEP, 15, 1), AZF3328_MIXER_VOL_SPECIAL("PC Speaker Playback Volume", IDX_MIXER_PCBEEP, 0x0f, 1, 1), AZF3328_MIXER_SWITCH("Video Playback Switch", IDX_MIXER_VIDEO, 15, 1), AZF3328_MIXER_VOL_STEREO("Video Playback Volume", IDX_MIXER_VIDEO, 0x1f, 1), AZF3328_MIXER_SWITCH("Aux Playback Switch", IDX_MIXER_AUX, 15, 1), AZF3328_MIXER_VOL_STEREO("Aux Playback Volume", IDX_MIXER_AUX, 0x1f, 1), AZF3328_MIXER_SWITCH("Modem Playback Switch", IDX_MIXER_MODEMOUT, 15, 1), AZF3328_MIXER_VOL_MONO("Modem Playback Volume", IDX_MIXER_MODEMOUT, 0x1f, 1), AZF3328_MIXER_SWITCH("Modem Capture Switch", IDX_MIXER_MODEMIN, 15, 1), AZF3328_MIXER_VOL_MONO("Modem Capture Volume", IDX_MIXER_MODEMIN, 0x1f, 1), AZF3328_MIXER_ENUM("Mic Select", IDX_MIXER_ADVCTL2, 2, 8), AZF3328_MIXER_ENUM("Mono Output Select", IDX_MIXER_ADVCTL2, 2, 9), AZF3328_MIXER_ENUM("PCM Output Route", IDX_MIXER_ADVCTL2, 2, 15), /* PCM Out Path, place in front since it controls *both* 3D and Bass/Treble! */ AZF3328_MIXER_VOL_SPECIAL("Tone Control - Treble", IDX_MIXER_BASSTREBLE, 0x07, 1, 0), AZF3328_MIXER_VOL_SPECIAL("Tone Control - Bass", IDX_MIXER_BASSTREBLE, 0x07, 9, 0), AZF3328_MIXER_SWITCH("3D Control - Switch", IDX_MIXER_ADVCTL2, 13, 0), AZF3328_MIXER_VOL_SPECIAL("3D Control - Width", IDX_MIXER_ADVCTL1, 0x07, 1, 0), /* "3D Width" */ AZF3328_MIXER_VOL_SPECIAL("3D Control - Depth", IDX_MIXER_ADVCTL1, 0x03, 8, 0), /* "Hifi 3D" */#if MIXER_TESTING AZF3328_MIXER_SWITCH("0", IDX_MIXER_ADVCTL2, 0, 0), AZF3328_MIXER_SWITCH("1", IDX_MIXER_ADVCTL2, 1, 0), AZF3328_MIXER_SWITCH("2", IDX_MIXER_ADVCTL2, 2, 0), AZF3328_MIXER_SWITCH("3", IDX_MIXER_ADVCTL2, 3, 0), AZF3328_MIXER_SWITCH("4", IDX_MIXER_ADVCTL2, 4, 0), AZF3328_MIXER_SWITCH("5", IDX_MIXER_ADVCTL2, 5, 0), AZF3328_MIXER_SWITCH("6", IDX_MIXER_ADVCTL2, 6, 0), AZF3328_MIXER_SWITCH("7", IDX_MIXER_ADVCTL2, 7, 0), AZF3328_MIXER_SWITCH("8", IDX_MIXER_ADVCTL2, 8, 0), AZF3328_MIXER_SWITCH("9", IDX_MIXER_ADVCTL2, 9, 0), AZF3328_MIXER_SWITCH("10", IDX_MIXER_ADVCTL2, 10, 0), AZF3328_MIXER_SWITCH("11", IDX_MIXER_ADVCTL2, 11, 0), AZF3328_MIXER_SWITCH("12", IDX_MIXER_ADVCTL2, 12, 0), AZF3328_MIXER_SWITCH("13", IDX_MIXER_ADVCTL2, 13, 0), AZF3328_MIXER_SWITCH("14", IDX_MIXER_ADVCTL2, 14, 0), AZF3328_MIXER_SWITCH("15", IDX_MIXER_ADVCTL2, 15, 0),#endif};static u16 __devinitdata snd_azf3328_init_values[][2] = { { IDX_MIXER_PLAY_MASTER, MIXER_MUTE_MASK|0x1f1f }, { IDX_MIXER_MODEMOUT, MIXER_MUTE_MASK|0x1f1f }, { IDX_MIXER_BASSTREBLE, 0x0000 }, { IDX_MIXER_PCBEEP, MIXER_MUTE_MASK|0x1f1f }, { IDX_MIXER_MODEMIN, MIXER_MUTE_MASK|0x1f1f }, { IDX_MIXER_MIC, MIXER_MUTE_MASK|0x001f }, { IDX_MIXER_LINEIN, MIXER_MUTE_MASK|0x1f1f }, { IDX_MIXER_CDAUDIO, MIXER_MUTE_MASK|0x1f1f }, { IDX_MIXER_VIDEO, MIXER_MUTE_MASK|0x1f1f }, { IDX_MIXER_AUX, MIXER_MUTE_MASK|0x1f1f }, { IDX_MIXER_WAVEOUT, MIXER_MUTE_MASK|0x1f1f }, { IDX_MIXER_FMSYNTH, MIXER_MUTE_MASK|0x1f1f }, { IDX_MIXER_REC_VOLUME, MIXER_MUTE_MASK|0x0707 },};static int __devinitsnd_azf3328_mixer_new(struct snd_azf3328 *chip){ struct snd_card *card; const struct snd_kcontrol_new *sw; unsigned int idx; int err; snd_azf3328_dbgcallenter(); snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); card = chip->card; /* mixer reset */ snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000); /* mute and zero volume channels */ for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_init_values); idx++) { snd_azf3328_mixer_outw(chip, snd_azf3328_init_values[idx][0], snd_azf3328_init_values[idx][1]); } /* add mixer controls */ sw = snd_azf3328_mixer_controls; for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_mixer_controls); idx++, sw++) { if ((err = snd_ctl_add(chip->card, snd_ctl_new1(sw, chip))) < 0) return err; } snd_component_add(card, "AZF3328 mixer"); strcpy(card->mixername, "AZF3328 mixer"); snd_azf3328_dbgcallleave(); return 0;}static intsnd_azf3328_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params){ int res; snd_azf3328_dbgcallenter(); res = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); snd_azf3328_dbgcallleave(); return res;}static intsnd_azf3328_hw_free(struct snd_pcm_substream *substream){ snd_azf3328_dbgcallenter(); snd_pcm_lib_free_pages(substream); snd_azf3328_dbgcallleave(); return 0;}static voidsnd_azf3328_setfmt(struct snd_azf3328 *chip, unsigned int reg, unsigned int bitrate, unsigned int format_width, unsigned int channels){ u16 val = 0xff00; unsigned long flags; snd_azf3328_dbgcallenter(); switch (bitrate) { case 4000: val |= SOUNDFORMAT_FREQ_SUSPECTED_4000; break; case 4800: val |= SOUNDFORMAT_FREQ_SUSPECTED_4800; break; case 5512: val |= SOUNDFORMAT_FREQ_5510; break; /* the AZF3328 names it "5510" for some strange reason */ case 6620: val |= SOUNDFORMAT_FREQ_6620; break; case 8000: val |= SOUNDFORMAT_FREQ_8000; break; case 9600: val |= SOUNDFORMAT_FREQ_9600; break; case 11025: val |= SOUNDFORMAT_FREQ_11025; break; case 13240: val |= SOUNDFORMAT_FREQ_SUSPECTED_13240; break; case 16000: val |= SOUNDFORMAT_FREQ_16000; break; case 22050: val |= SOUNDFORMAT_FREQ_22050; break; case 32000: val |= SOUNDFORMAT_FREQ_32000; break; case 44100: val |= SOUNDFORMAT_FREQ_44100; break; case 48000: val |= SOUNDFORMAT_FREQ_48000; break; case 66200: val |= SOUNDFORMAT_FREQ_SUSPECTED_66200; break; default: snd_printk(KERN_WARNING "unknown bitrate %d, assuming 44.1kHz!\n", bitrate); val |= SOUNDFORMAT_FREQ_44100; break; } /* val = 0xff07; 3m27.993s (65301Hz; -> 64000Hz???) hmm, 66120, 65967, 66123 */ /* val = 0xff09; 17m15.098s (13123,478Hz; -> 12000Hz???) hmm, 13237.2Hz? */ /* val = 0xff0a; 47m30.599s (4764,891Hz; -> 4800Hz???) yup, 4803Hz */ /* val = 0xff0c; 57m0.510s (4010,263Hz; -> 4000Hz???) yup, 4003Hz */ /* val = 0xff05; 5m11.556s (... -> 44100Hz) */ /* val = 0xff03; 10m21.529s (21872,463Hz; -> 22050Hz???) */ /* val = 0xff0f; 20m41.883s (10937,993Hz; -> 11025Hz???) */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -