📄 azt3328.c
字号:
{ .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 int snd_azf3328_info_mixer(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){ azf3328_mixer_reg_t 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 int snd_azf3328_get_mixer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ azf3328_t *chip = snd_kcontrol_chip(kcontrol); azf3328_mixer_reg_t reg; unsigned int oreg, val; snd_azf3328_dbgcallenter(); snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); oreg = inw(chip->mixer_port + 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 int snd_azf3328_put_mixer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ azf3328_t *chip = snd_kcontrol_chip(kcontrol); azf3328_mixer_reg_t reg; unsigned int oreg, nreg, val; snd_azf3328_dbgcallenter(); snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); oreg = inw(chip->mixer_port + 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, SET_CHAN_LEFT|SET_CHAN_RIGHT, 0); /* just set both channels, doesn't matter */ else outw(nreg, chip->mixer_port + reg.reg); 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, inw(chip->mixer_port + reg.reg)); snd_azf3328_dbgcallleave(); return (nreg != oreg);}static int snd_azf3328_info_mixer_enum(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo){ azf3328_mixer_reg_t reg; static char *texts1[2] = { "ModemOut1", "ModemOut2" }; static char *texts2[2] = { "MonoSelectSource1", "MonoSelectSource2" }; static char *texts3[8] = { "Mic", "CD", "Video", "Aux", "Line", "Mix", "Mix Mono", "Phone" }; 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) { if (reg.lchan_shift == 8) /* modem out sel */ strcpy(uinfo->value.enumerated.name, texts1[uinfo->value.enumerated.item]); else /* mono sel source */ strcpy(uinfo->value.enumerated.name, texts2[uinfo->value.enumerated.item]); } else strcpy(uinfo->value.enumerated.name, texts3[uinfo->value.enumerated.item]); return 0;}static int snd_azf3328_get_mixer_enum(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ azf3328_mixer_reg_t reg; azf3328_t *chip = snd_kcontrol_chip(kcontrol); unsigned short val; snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); val = inw(chip->mixer_port + 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 int snd_azf3328_put_mixer_enum(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ azf3328_mixer_reg_t reg; azf3328_t *chip = snd_kcontrol_chip(kcontrol); unsigned int oreg, nreg, val; snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); oreg = inw(chip->mixer_port + 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); } outw(val, chip->mixer_port + reg.reg); nreg = val; snd_azf3328_dbgmixer("put_enum: %02x to %04x, oreg %04x\n", reg.reg, val, oreg); return (nreg != oreg);}static snd_kcontrol_new_t 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 Playback 3D Bypass", 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("PCBeep Playback Switch", IDX_MIXER_PCBEEP, 15, 1), AZF3328_MIXER_VOL_SPECIAL("PCBeep 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("Modem Out Select", IDX_MIXER_ADVCTL2, 2, 8), AZF3328_MIXER_ENUM("Mono Select Source", IDX_MIXER_ADVCTL2, 2, 9), 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 - Toggle", IDX_MIXER_ADVCTL2, 13, 0), AZF3328_MIXER_VOL_SPECIAL("3D Control - Volume", IDX_MIXER_ADVCTL1, 0x07, 1, 0), /* "3D Width" */ AZF3328_MIXER_VOL_SPECIAL("3D Control - Space", 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};#define AZF3328_INIT_VALUES (sizeof(snd_azf3328_init_values)/sizeof(unsigned int)/2)static unsigned int 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 __devinit snd_azf3328_mixer_new(azf3328_t *chip){ snd_card_t *card; snd_kcontrol_new_t *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_write(chip, IDX_MIXER_RESET, 0x0, WORD_VALUE); /* mute and zero volume channels */ for (idx = 0; idx < AZF3328_INIT_VALUES; idx++) { snd_azf3328_mixer_write(chip, snd_azf3328_init_values[idx][0], snd_azf3328_init_values[idx][1], WORD_VALUE); } /* 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 int snd_azf3328_hw_params(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * 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 int snd_azf3328_hw_free(snd_pcm_substream_t * substream){ snd_azf3328_dbgcallenter(); snd_pcm_lib_free_pages(substream); snd_azf3328_dbgcallleave(); return 0;}static void snd_azf3328_setfmt(azf3328_t *chip, unsigned int reg, unsigned int bitrate, unsigned int format_width, unsigned int channels){ unsigned int val = 0xff00; unsigned long flags; snd_azf3328_dbgcallenter(); switch (bitrate) { case 5512: val |= 0x0d; break; /* the AZF3328 names it "5510" for some strange reason */ case 6620: val |= 0x0b; break; case 8000: val |= 0x00; break; case 9600: val |= 0x08; break; case 11025: val |= 0x01; break; case 16000: val |= 0x02; break; case 22050: val |= 0x03; break; case 32000: val |= 0x04; break; case 44100: val |= 0x05; break; case 48000: val |= 0x06; break; case 64000: val |= 0x07; break; default: snd_printk("unknown bitrate %d, assuming 44.1kHz!\n", bitrate); val |= 0x05; /* 44100 */ break; } /* val = 0xff07; 3m27.993s (65301Hz; -> 64000Hz???) */ /* val = 0xff09; 17m15.098s (13123,478Hz; -> 12000Hz???) */ /* val = 0xff0a; 47m30.599s (4764,891Hz; -> 4800Hz???) */ /* val = 0xff0c; 57m0.510s (4010,263Hz; -> 4000Hz???) */ /* val = 0xff05; 5m11.556s (... -> 44100Hz) */ /* val = 0xff03; 10m21.529s (21872,463Hz; -> 22050Hz???) */ /* val = 0xff0f; 20m41.883s (10937,993Hz; -> 11025Hz???) */ /* val = 0xff0d; 41m23.135s (5523,600Hz; -> 5512Hz???) */ /* val = 0xff0e; 28m30.777s (8017Hz; -> 8000Hz???) */ if (channels == 2) val |= SOUNDFORMAT_FLAG_2CHANNELS; if (format_width == 16) val |= SOUNDFORMAT_FLAG_16BIT; spin_lock_irqsave(&chip->reg_lock, flags); /* set bitrate/format */ outw(val, chip->codec_port+reg); /* changing the bitrate/format settings switches off the * audio output with an annoying click in case of 8/16bit format change * (maybe shutting down DAC/ADC?), thus immediately * do some tweaking to reenable it and get rid of the clicking * (FIXME: yes, it works, but what exactly am I doing here?? :) * FIXME: does this have some side effects for full-duplex * or other dramatic side effects? */ if (reg == IDX_IO_PLAY_SOUNDFORMAT) /* only do it for playback */ outw(inw(chip->codec_port + IDX_IO_PLAY_FLAGS)|DMA_PLAY_SOMETHING1|DMA_PLAY_SOMETHING2|SOMETHING_ALMOST_ALWAYS_SET|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE, chip->codec_port + IDX_IO_PLAY_FLAGS); spin_unlock_irqrestore(&chip->reg_lock, flags); snd_azf3328_dbgcallleave();}static void snd_azf3328_setdmaa(azf3328_t *chip, long unsigned int addr, unsigned int count, unsigned int size, int do_recording){ long unsigned int addr1; long unsigned int addr2; unsigned int count1; unsigned int count2; unsigned long flags; int reg_offs = do_recording ? 0x20 : 0x00; snd_azf3328_dbgcallenter(); /* AZF3328 uses a two buffer pointer DMA playback approach */ if (!chip->is_playing) { addr1 = addr; addr2 = addr+(size/2); count1 = (size/2)-1; count2 = (size/2)-1;#if DEBUG_PLAY_REC snd_azf3328_dbgplay("setting dma: buf1 %08lx[%d], buf2 %08lx[%d]\n", addr1, count1, addr2, count2);#endif spin_lock_irqsave(&chip->reg_lock, flags); outl(addr1, chip->codec_port+reg_offs+IDX_IO_PLAY_DMA_START_1); outl(addr2, chip->codec_port+reg_offs+IDX_IO_PLAY_DMA_START_2); outw(count1, chip->codec_port+reg_offs+IDX_IO_PLAY_DMA_LEN_1); outw(count2, chip->codec_port+reg_offs+IDX_IO_PLAY_DMA_LEN_2); spin_unlock_irqrestore(&chip->reg_lock, flags); } snd_azf3328_dbgcallleave();}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -