⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 vx_mixer.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Driver for Digigram VX soundcards * * Common mixer part * * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> * *   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 <sound/core.h>#include <sound/control.h>#include <sound/vx_core.h>#include "vx_cmd.h"/* * write a codec data (24bit) */static void vx_write_codec_reg(vx_core_t *chip, int codec, unsigned int data){	unsigned long flags;	snd_assert(chip->ops->write_codec, return);	if (chip->chip_status & VX_STAT_IS_STALE)		return;	spin_lock_irqsave(&chip->lock, flags);	chip->ops->write_codec(chip, codec, data);	spin_unlock_irqrestore(&chip->lock, flags);}/* * Data type used to access the Codec */typedef union {	u32 l;#ifdef SNDRV_BIG_ENDIAN	struct w {		u16 h;		u16 l;	} w;	struct b {		u8 hh;		u8 mh;		u8 ml;		u8 ll;	} b;#else /* LITTLE_ENDIAN */	struct w {		u16 l;		u16 h;	} w;	struct b {		u8 ll;		u8 ml;		u8 mh;		u8 hh;	} b;#endif} vx_codec_data_t;#define SET_CDC_DATA_SEL(di,s)          ((di).b.mh = (u8) (s))#define SET_CDC_DATA_REG(di,r)          ((di).b.ml = (u8) (r))#define SET_CDC_DATA_VAL(di,d)          ((di).b.ll = (u8) (d))#define SET_CDC_DATA_INIT(di)           ((di).l = 0L, SET_CDC_DATA_SEL(di,XX_CODEC_SELECTOR))/* * set up codec register and write the value * @codec: the codec id, 0 or 1 * @reg: register index * @val: data value */static void vx_set_codec_reg(vx_core_t *chip, int codec, int reg, int val){	vx_codec_data_t data;	/* DAC control register */	SET_CDC_DATA_INIT(data);	SET_CDC_DATA_REG(data, reg);	SET_CDC_DATA_VAL(data, val);	vx_write_codec_reg(chip, codec, data.l);}/* * vx_set_analog_output_level - set the output attenuation level * @codec: the output codec, 0 or 1.  (1 for VXP440 only) * @left: left output level, 0 = mute * @right: right output level */static void vx_set_analog_output_level(vx_core_t *chip, int codec, int left, int right){	left  = chip->hw->output_level_max - left;	right = chip->hw->output_level_max - right;	if (chip->ops->akm_write) {		chip->ops->akm_write(chip, XX_CODEC_LEVEL_LEFT_REGISTER, left);		chip->ops->akm_write(chip, XX_CODEC_LEVEL_RIGHT_REGISTER, right);	} else {		/* convert to attenuation level: 0 = 0dB (max), 0xe3 = -113.5 dB (min) */		vx_set_codec_reg(chip, codec, XX_CODEC_LEVEL_LEFT_REGISTER, left);		vx_set_codec_reg(chip, codec, XX_CODEC_LEVEL_RIGHT_REGISTER, right);	}}/* * vx_toggle_dac_mute -  mute/unmute DAC * @mute: 0 = unmute, 1 = mute */#define DAC_ATTEN_MIN	0x08#define DAC_ATTEN_MAX	0x38void vx_toggle_dac_mute(vx_core_t *chip, int mute){	unsigned int i;	for (i = 0; i < chip->hw->num_codecs; i++) {		if (chip->ops->akm_write)			chip->ops->akm_write(chip, XX_CODEC_DAC_CONTROL_REGISTER, mute); /* XXX */		else			vx_set_codec_reg(chip, i, XX_CODEC_DAC_CONTROL_REGISTER,					 mute ? DAC_ATTEN_MAX : DAC_ATTEN_MIN);	}}/* * vx_reset_codec - reset and initialize the codecs */void vx_reset_codec(vx_core_t *chip, int cold_reset){	unsigned int i;	int port = chip->type >= VX_TYPE_VXPOCKET ? 0x75 : 0x65;	chip->ops->reset_codec(chip);	/* AKM codecs should be initialized in reset_codec callback */	if (! chip->ops->akm_write) {		/* initialize old codecs */		for (i = 0; i < chip->hw->num_codecs; i++) {			/* DAC control register (change level when zero crossing + mute) */			vx_set_codec_reg(chip, i, XX_CODEC_DAC_CONTROL_REGISTER, DAC_ATTEN_MAX);			/* ADC control register */			vx_set_codec_reg(chip, i, XX_CODEC_ADC_CONTROL_REGISTER, 0x00);			/* Port mode register */			vx_set_codec_reg(chip, i, XX_CODEC_PORT_MODE_REGISTER, port);			/* Clock control register */			vx_set_codec_reg(chip, i, XX_CODEC_CLOCK_CONTROL_REGISTER, 0x00);		}	}	/* mute analog output */	for (i = 0; i < chip->hw->num_codecs; i++) {		chip->output_level[i][0] = 0;		chip->output_level[i][1] = 0;		vx_set_analog_output_level(chip, i, 0, 0);	}}/* * change the audio input source * @src: the target source (VX_AUDIO_SRC_XXX) */static void vx_change_audio_source(vx_core_t *chip, int src){	unsigned long flags;	if (chip->chip_status & VX_STAT_IS_STALE)		return;	spin_lock_irqsave(&chip->lock, flags);	chip->ops->change_audio_source(chip, src);	spin_unlock_irqrestore(&chip->lock, flags);}/* * change the audio source if necessary and possible * returns 1 if the source is actually changed. */int vx_sync_audio_source(vx_core_t *chip){	if (chip->audio_source_target == chip->audio_source ||	    chip->pcm_running)		return 0;	vx_change_audio_source(chip, chip->audio_source_target);	chip->audio_source = chip->audio_source_target;	return 1;}/* * audio level, mute, monitoring */struct vx_audio_level {	unsigned int has_level: 1;	unsigned int has_monitor_level: 1;	unsigned int has_mute: 1;	unsigned int has_monitor_mute: 1;	unsigned int mute;	unsigned int monitor_mute;	short level;	short monitor_level;};static int vx_adjust_audio_level(vx_core_t *chip, int audio, int capture,				 struct vx_audio_level *info){	struct vx_rmh rmh;	if (chip->chip_status & VX_STAT_IS_STALE)		return -EBUSY;        vx_init_rmh(&rmh, CMD_AUDIO_LEVEL_ADJUST);	if (capture)		rmh.Cmd[0] |= COMMAND_RECORD_MASK;	/* Add Audio IO mask */	rmh.Cmd[1] = 1 << audio;	rmh.Cmd[2] = 0;	if (info->has_level) {		rmh.Cmd[0] |=  VALID_AUDIO_IO_DIGITAL_LEVEL;		rmh.Cmd[2] |= info->level;        }	if (info->has_monitor_level) {		rmh.Cmd[0] |=  VALID_AUDIO_IO_MONITORING_LEVEL;		rmh.Cmd[2] |= ((unsigned int)info->monitor_level << 10);        }	if (info->has_mute) { 		rmh.Cmd[0] |= VALID_AUDIO_IO_MUTE_LEVEL;		if (info->mute)			rmh.Cmd[2] |= AUDIO_IO_HAS_MUTE_LEVEL;	}	if (info->has_monitor_mute) {		/* validate flag for M2 at least to unmute it */ 		rmh.Cmd[0] |=  VALID_AUDIO_IO_MUTE_MONITORING_1 | VALID_AUDIO_IO_MUTE_MONITORING_2;		if (info->monitor_mute)			rmh.Cmd[2] |= AUDIO_IO_HAS_MUTE_MONITORING_1;	}	return vx_send_msg(chip, &rmh);}    #if 0 // not usedstatic int vx_read_audio_level(vx_core_t *chip, int audio, int capture,			       struct vx_audio_level *info){	int err;	struct vx_rmh rmh;	memset(info, 0, sizeof(*info));        vx_init_rmh(&rmh, CMD_GET_AUDIO_LEVELS);	if (capture)		rmh.Cmd[0] |= COMMAND_RECORD_MASK;	/* Add Audio IO mask */	rmh.Cmd[1] = 1 << audio;	err = vx_send_msg(chip, &rmh);	if (err < 0)		return err;	info.level = rmh.Stat[0] & MASK_DSP_WORD_LEVEL;	info.monitor_level = (rmh.Stat[0] >> 10) & MASK_DSP_WORD_LEVEL;	info.mute = (rmh.Stat[i] & AUDIO_IO_HAS_MUTE_LEVEL) ? 1 : 0;	info.monitor_mute = (rmh.Stat[i] & AUDIO_IO_HAS_MUTE_MONITORING_1) ? 1 : 0;	return 0;}#endif // not used/* * set the monitoring level and mute state of the given audio * no more static, because must be called from vx_pcm to demute monitoring */int vx_set_monitor_level(vx_core_t *chip, int audio, int level, int active){	struct vx_audio_level info;	memset(&info, 0, sizeof(info));	info.has_monitor_level = 1;	info.monitor_level = level;	info.has_monitor_mute = 1;	info.monitor_mute = !active;	chip->audio_monitor[audio] = level;	chip->audio_monitor_active[audio] = active;	return vx_adjust_audio_level(chip, audio, 0, &info); /* playback only */}/* * set the mute status of the given audio */static int vx_set_audio_switch(vx_core_t *chip, int audio, int active){	struct vx_audio_level info;	memset(&info, 0, sizeof(info));	info.has_mute = 1;	info.mute = !active;	chip->audio_active[audio] = active;	return vx_adjust_audio_level(chip, audio, 0, &info); /* playback only */}/* * set the mute status of the given audio */static int vx_set_audio_gain(vx_core_t *chip, int audio, int capture, int level){	struct vx_audio_level info;	memset(&info, 0, sizeof(info));	info.has_level = 1;	info.level = level;	chip->audio_gain[capture][audio] = level;	return vx_adjust_audio_level(chip, audio, capture, &info);}/* * reset all audio levels */static void vx_reset_audio_levels(vx_core_t *chip){	unsigned int i, c;	struct vx_audio_level info;	memset(chip->audio_gain, 0, sizeof(chip->audio_gain));	memset(chip->audio_active, 0, sizeof(chip->audio_active));	memset(chip->audio_monitor, 0, sizeof(chip->audio_monitor));	memset(chip->audio_monitor_active, 0, sizeof(chip->audio_monitor_active));	for (c = 0; c < 2; c++) {		for (i = 0; i < chip->hw->num_ins * 2; i++) {			memset(&info, 0, sizeof(info));			if (c == 0) {				info.has_monitor_level = 1;				info.has_mute = 1;				info.has_monitor_mute = 1;			}			info.has_level = 1;			info.level = CVAL_0DB; /* default: 0dB */			vx_adjust_audio_level(chip, i, c, &info);			chip->audio_gain[c][i] = CVAL_0DB;			chip->audio_monitor[i] = CVAL_0DB;		}	}}/* * VU, peak meter record */#define VU_METER_CHANNELS	2struct vx_vu_meter {	int saturated;	int vu_level;	int peak_level;};/* * get the VU and peak meter values * @audio: the audio index * @capture: 0 = playback, 1 = capture operation * @info: the array of vx_vu_meter records (size = 2). */static int vx_get_audio_vu_meter(vx_core_t *chip, int audio, int capture, struct vx_vu_meter *info){	struct vx_rmh rmh;	int i, err;	if (chip->chip_status & VX_STAT_IS_STALE)		return -EBUSY;	vx_init_rmh(&rmh, CMD_AUDIO_VU_PIC_METER);	rmh.LgStat += 2 * VU_METER_CHANNELS;	if (capture)		rmh.Cmd[0] |= COMMAND_RECORD_MASK;            /* Add Audio IO mask */	rmh.Cmd[1] = 0;	for (i = 0; i < VU_METER_CHANNELS; i++)		rmh.Cmd[1] |= 1 << (audio + i);	err = vx_send_msg(chip, &rmh);	if (err < 0)		return err;	/* Read response */	for (i = 0; i < 2 * VU_METER_CHANNELS; i +=2) {		info->saturated = (rmh.Stat[0] & (1 << (audio + i))) ? 1 : 0;		info->vu_level = rmh.Stat[i + 1];		info->peak_level = rmh.Stat[i + 2];		info++;	}	return 0;}   /* * control API entries *//* * output level control */static int vx_output_level_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){	vx_core_t *chip = snd_kcontrol_chip(kcontrol);	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;	uinfo->count = 2;	uinfo->value.integer.min = 0;	uinfo->value.integer.max = chip->hw->output_level_max;	return 0;}static int vx_output_level_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){	vx_core_t *chip = snd_kcontrol_chip(kcontrol);	int codec = kcontrol->id.index;	down(&chip->mixer_mutex);	ucontrol->value.integer.value[0] = chip->output_level[codec][0];	ucontrol->value.integer.value[1] = chip->output_level[codec][1];	up(&chip->mixer_mutex);	return 0;}static int vx_output_level_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){	vx_core_t *chip = snd_kcontrol_chip(kcontrol);	int codec = kcontrol->id.index;	down(&chip->mixer_mutex);	if (ucontrol->value.integer.value[0] != chip->output_level[codec][0] ||	    ucontrol->value.integer.value[1] != chip->output_level[codec][1]) {		vx_set_analog_output_level(chip, codec,					   ucontrol->value.integer.value[0],					   ucontrol->value.integer.value[1]);		chip->output_level[codec][0] = ucontrol->value.integer.value[0];		chip->output_level[codec][1] = ucontrol->value.integer.value[1];		up(&chip->mixer_mutex);		return 1;	}	up(&chip->mixer_mutex);	return 0;}static snd_kcontrol_new_t vx_control_output_level = {	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,	.name =		"Master Playback Volume",	.info =		vx_output_level_info,	.get =		vx_output_level_get,	.put =		vx_output_level_put,};/* * audio source select */static int vx_audio_src_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){	static char *texts_mic[3] = {		"Digital", "Line", "Mic"	};	static char *texts_vx2[2] = {		"Digital", "Analog"	};	vx_core_t *chip = snd_kcontrol_chip(kcontrol);	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;	uinfo->count = 1;	if (chip->type >= VX_TYPE_VXPOCKET) {		uinfo->value.enumerated.items = 3;		if (uinfo->value.enumerated.item > 2)			uinfo->value.enumerated.item = 2;		strcpy(uinfo->value.enumerated.name,		       texts_mic[uinfo->value.enumerated.item]);	} else {		uinfo->value.enumerated.items = 2;		if (uinfo->value.enumerated.item > 1)			uinfo->value.enumerated.item = 1;		strcpy(uinfo->value.enumerated.name,		       texts_vx2[uinfo->value.enumerated.item]);	}	return 0;}static int vx_audio_src_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){	vx_core_t *chip = snd_kcontrol_chip(kcontrol);	ucontrol->value.enumerated.item[0] = chip->audio_source_target;	return 0;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -