diff options
Diffstat (limited to 'sound/usb/mixer_quirks.c')
| -rw-r--r-- | sound/usb/mixer_quirks.c | 472 | 
1 files changed, 445 insertions, 27 deletions
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index ab125ee0b0f..41f4b691192 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -42,6 +42,77 @@  extern struct snd_kcontrol_new *snd_usb_feature_unit_ctl; +/* private_free callback */ +static void usb_mixer_elem_free(struct snd_kcontrol *kctl) +{ +	kfree(kctl->private_data); +	kctl->private_data = NULL; +} + +/* This function allows for the creation of standard UAC controls. + * See the quirks for M-Audio FTUs or Ebox-44. + * If you don't want to set a TLV callback pass NULL. + * + * Since there doesn't seem to be a devices that needs a multichannel + * version, we keep it mono for simplicity. + */ +static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer, +				unsigned int unitid, +				unsigned int control, +				unsigned int cmask, +				int val_type, +				const char *name, +				snd_kcontrol_tlv_rw_t *tlv_callback) +{ +	int err; +	struct usb_mixer_elem_info *cval; +	struct snd_kcontrol *kctl; + +	cval = kzalloc(sizeof(*cval), GFP_KERNEL); +	if (!cval) +		return -ENOMEM; + +	cval->id = unitid; +	cval->mixer = mixer; +	cval->val_type = val_type; +	cval->channels = 1; +	cval->control = control; +	cval->cmask = cmask; + +	/* get_min_max() is called only for integer volumes later, +	 * so provide a short-cut for booleans */ +	cval->min = 0; +	cval->max = 1; +	cval->res = 0; +	cval->dBmin = 0; +	cval->dBmax = 0; + +	/* Create control */ +	kctl = snd_ctl_new1(snd_usb_feature_unit_ctl, cval); +	if (!kctl) { +		kfree(cval); +		return -ENOMEM; +	} + +	/* Set name */ +	snprintf(kctl->id.name, sizeof(kctl->id.name), name); +	kctl->private_free = usb_mixer_elem_free; + +	/* set TLV */ +	if (tlv_callback) { +		kctl->tlv.c = tlv_callback; +		kctl->vd[0].access |= +			SNDRV_CTL_ELEM_ACCESS_TLV_READ | +			SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; +	} +	/* Add control to mixer */ +	err = snd_usb_mixer_add_control(mixer, kctl); +	if (err < 0) +		return err; + +	return 0; +} +  /*   * Sound Blaster remote control configuration   * @@ -495,60 +566,218 @@ static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer,  }  /* M-Audio FastTrack Ultra quirks */ +/* FTU Effect switch */ +struct snd_ftu_eff_switch_priv_val { +	struct usb_mixer_interface *mixer; +	int cached_value; +	int is_cached; +}; -/* private_free callback */ -static void usb_mixer_elem_free(struct snd_kcontrol *kctl) +static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol, +					struct snd_ctl_elem_info *uinfo)  { -	kfree(kctl->private_data); -	kctl->private_data = NULL; +	static const char *texts[8] = {"Room 1", +				       "Room 2", +				       "Room 3", +				       "Hall 1", +				       "Hall 2", +				       "Plate", +				       "Delay", +				       "Echo" +	}; + +	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; +	uinfo->count = 1; +	uinfo->value.enumerated.items = 8; +	if (uinfo->value.enumerated.item > 7) +		uinfo->value.enumerated.item = 7; +	strcpy(uinfo->value.enumerated.name, +		texts[uinfo->value.enumerated.item]); + +	return 0;  } -static int snd_maudio_ftu_create_ctl(struct usb_mixer_interface *mixer, -				     int in, int out, const char *name) +static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl, +					struct snd_ctl_elem_value *ucontrol)  { -	struct usb_mixer_elem_info *cval; +	struct snd_usb_audio *chip; +	struct usb_mixer_interface *mixer; +	struct snd_ftu_eff_switch_priv_val *pval; +	int err; +	unsigned char value[2]; + +	const int id = 6; +	const int validx = 1; +	const int val_len = 2; + +	value[0] = 0x00; +	value[1] = 0x00; + +	pval = (struct snd_ftu_eff_switch_priv_val *) +		kctl->private_value; + +	if (pval->is_cached) { +		ucontrol->value.enumerated.item[0] = pval->cached_value; +		return 0; +	} + +	mixer = (struct usb_mixer_interface *) pval->mixer; +	if (snd_BUG_ON(!mixer)) +		return -EINVAL; + +	chip = (struct snd_usb_audio *) mixer->chip; +	if (snd_BUG_ON(!chip)) +		return -EINVAL; + + +	err = snd_usb_ctl_msg(chip->dev, +			usb_rcvctrlpipe(chip->dev, 0), UAC_GET_CUR, +			USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, +			validx << 8, snd_usb_ctrl_intf(chip) | (id << 8), +			value, val_len); +	if (err < 0) +		return err; + +	ucontrol->value.enumerated.item[0] = value[0]; +	pval->cached_value = value[0]; +	pval->is_cached = 1; + +	return 0; +} + +static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl, +					struct snd_ctl_elem_value *ucontrol) +{ +	struct snd_usb_audio *chip; +	struct snd_ftu_eff_switch_priv_val *pval; + +	struct usb_mixer_interface *mixer; +	int changed, cur_val, err, new_val; +	unsigned char value[2]; + + +	const int id = 6; +	const int validx = 1; +	const int val_len = 2; + +	changed = 0; + +	pval = (struct snd_ftu_eff_switch_priv_val *) +		kctl->private_value; +	cur_val = pval->cached_value; +	new_val = ucontrol->value.enumerated.item[0]; + +	mixer = (struct usb_mixer_interface *) pval->mixer; +	if (snd_BUG_ON(!mixer)) +		return -EINVAL; + +	chip = (struct snd_usb_audio *) mixer->chip; +	if (snd_BUG_ON(!chip)) +		return -EINVAL; + +	if (!pval->is_cached) { +		/* Read current value */ +		err = snd_usb_ctl_msg(chip->dev, +				usb_rcvctrlpipe(chip->dev, 0), UAC_GET_CUR, +				USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, +				validx << 8, snd_usb_ctrl_intf(chip) | (id << 8), +				value, val_len); +		if (err < 0) +			return err; + +		cur_val = value[0]; +		pval->cached_value = cur_val; +		pval->is_cached = 1; +	} +	/* update value if needed */ +	if (cur_val != new_val) { +		value[0] = new_val; +		value[1] = 0; +		err = snd_usb_ctl_msg(chip->dev, +				usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR, +				USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, +				validx << 8, snd_usb_ctrl_intf(chip) | (id << 8), +				value, val_len); +		if (err < 0) +			return err; + +		pval->cached_value = new_val; +		pval->is_cached = 1; +		changed = 1; +	} + +	return changed; +} + +static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer) +{ +	static struct snd_kcontrol_new template = { +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "Effect Program Switch", +		.index = 0, +		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, +		.info = snd_ftu_eff_switch_info, +		.get = snd_ftu_eff_switch_get, +		.put = snd_ftu_eff_switch_put +	}; + +	int err;  	struct snd_kcontrol *kctl; +	struct snd_ftu_eff_switch_priv_val *pval; -	cval = kzalloc(sizeof(*cval), GFP_KERNEL); -	if (!cval) +	pval = kzalloc(sizeof(*pval), GFP_KERNEL); +	if (!pval)  		return -ENOMEM; -	cval->id = 5; -	cval->mixer = mixer; -	cval->val_type = USB_MIXER_S16; -	cval->channels = 1; -	cval->control = out + 1; -	cval->cmask = 1 << in; +	pval->cached_value = 0; +	pval->is_cached = 0; +	pval->mixer = mixer; -	kctl = snd_ctl_new1(snd_usb_feature_unit_ctl, cval); +	template.private_value = (unsigned long) pval; +	kctl = snd_ctl_new1(&template, mixer->chip);  	if (!kctl) { -		kfree(cval); +		kfree(pval);  		return -ENOMEM;  	} -	snprintf(kctl->id.name, sizeof(kctl->id.name), name); -	kctl->private_free = usb_mixer_elem_free; -	return snd_usb_mixer_add_control(mixer, kctl); +	err = snd_ctl_add(mixer->chip->card, kctl); +	if (err < 0) +		return err; + +	return 0;  } -static int snd_maudio_ftu_create_mixer(struct usb_mixer_interface *mixer) +/* Create volume controls for FTU devices*/ +static int snd_ftu_create_volume_ctls(struct usb_mixer_interface *mixer)  {  	char name[64]; +	unsigned int control, cmask;  	int in, out, err; +	const unsigned int id = 5; +	const int val_type = USB_MIXER_S16; +  	for (out = 0; out < 8; out++) { +		control = out + 1;  		for (in = 0; in < 8; in++) { +			cmask = 1 << in;  			snprintf(name, sizeof(name), -				 "AIn%d - Out%d Capture Volume", in  + 1, out + 1); -			err = snd_maudio_ftu_create_ctl(mixer, in, out, name); +				"AIn%d - Out%d Capture Volume", +				in  + 1, out + 1); +			err = snd_create_std_mono_ctl(mixer, id, control, +							cmask, val_type, name, +							&snd_usb_mixer_vol_tlv);  			if (err < 0)  				return err;  		} -  		for (in = 8; in < 16; in++) { +			cmask = 1 << in;  			snprintf(name, sizeof(name), -				 "DIn%d - Out%d Playback Volume", in - 7, out + 1); -			err = snd_maudio_ftu_create_ctl(mixer, in, out, name); +				"DIn%d - Out%d Playback Volume", +				in - 7, out + 1); +			err = snd_create_std_mono_ctl(mixer, id, control, +							cmask, val_type, name, +							&snd_usb_mixer_vol_tlv);  			if (err < 0)  				return err;  		} @@ -557,6 +786,191 @@ static int snd_maudio_ftu_create_mixer(struct usb_mixer_interface *mixer)  	return 0;  } +/* This control needs a volume quirk, see mixer.c */ +static int snd_ftu_create_effect_volume_ctl(struct usb_mixer_interface *mixer) +{ +	static const char name[] = "Effect Volume"; +	const unsigned int id = 6; +	const int val_type = USB_MIXER_U8; +	const unsigned int control = 2; +	const unsigned int cmask = 0; + +	return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type, +					name, snd_usb_mixer_vol_tlv); +} + +/* This control needs a volume quirk, see mixer.c */ +static int snd_ftu_create_effect_duration_ctl(struct usb_mixer_interface *mixer) +{ +	static const char name[] = "Effect Duration"; +	const unsigned int id = 6; +	const int val_type = USB_MIXER_S16; +	const unsigned int control = 3; +	const unsigned int cmask = 0; + +	return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type, +					name, snd_usb_mixer_vol_tlv); +} + +/* This control needs a volume quirk, see mixer.c */ +static int snd_ftu_create_effect_feedback_ctl(struct usb_mixer_interface *mixer) +{ +	static const char name[] = "Effect Feedback Volume"; +	const unsigned int id = 6; +	const int val_type = USB_MIXER_U8; +	const unsigned int control = 4; +	const unsigned int cmask = 0; + +	return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type, +					name, NULL); +} + +static int snd_ftu_create_effect_return_ctls(struct usb_mixer_interface *mixer) +{ +	unsigned int cmask; +	int err, ch; +	char name[48]; + +	const unsigned int id = 7; +	const int val_type = USB_MIXER_S16; +	const unsigned int control = 7; + +	for (ch = 0; ch < 4; ++ch) { +		cmask = 1 << ch; +		snprintf(name, sizeof(name), +			"Effect Return %d Volume", ch + 1); +		err = snd_create_std_mono_ctl(mixer, id, control, +						cmask, val_type, name, +						snd_usb_mixer_vol_tlv); +		if (err < 0) +			return err; +	} + +	return 0; +} + +static int snd_ftu_create_effect_send_ctls(struct usb_mixer_interface *mixer) +{ +	unsigned int  cmask; +	int err, ch; +	char name[48]; + +	const unsigned int id = 5; +	const int val_type = USB_MIXER_S16; +	const unsigned int control = 9; + +	for (ch = 0; ch < 8; ++ch) { +		cmask = 1 << ch; +		snprintf(name, sizeof(name), +			"Effect Send AIn%d Volume", ch + 1); +		err = snd_create_std_mono_ctl(mixer, id, control, cmask, +						val_type, name, +						snd_usb_mixer_vol_tlv); +		if (err < 0) +			return err; +	} +	for (ch = 8; ch < 16; ++ch) { +		cmask = 1 << ch; +		snprintf(name, sizeof(name), +			"Effect Send DIn%d Volume", ch - 7); +		err = snd_create_std_mono_ctl(mixer, id, control, cmask, +						val_type, name, +						snd_usb_mixer_vol_tlv); +		if (err < 0) +			return err; +	} +	return 0; +} + +static int snd_ftu_create_mixer(struct usb_mixer_interface *mixer) +{ +	int err; + +	err = snd_ftu_create_volume_ctls(mixer); +	if (err < 0) +		return err; + +	err = snd_ftu_create_effect_switch(mixer); +	if (err < 0) +		return err; +	err = snd_ftu_create_effect_volume_ctl(mixer); +	if (err < 0) +		return err; + +	err = snd_ftu_create_effect_duration_ctl(mixer); +	if (err < 0) +		return err; + +	err = snd_ftu_create_effect_feedback_ctl(mixer); +	if (err < 0) +		return err; + +	err = snd_ftu_create_effect_return_ctls(mixer); +	if (err < 0) +		return err; + +	err = snd_ftu_create_effect_send_ctls(mixer); +	if (err < 0) +		return err; + +	return 0; +} + + +/* + * Create mixer for Electrix Ebox-44 + * + * The mixer units from this device are corrupt, and even where they + * are valid they presents mono controls as L and R channels of + * stereo. So we create a good mixer in code. + */ + +static int snd_ebox44_create_mixer(struct usb_mixer_interface *mixer) +{ +	int err; + +	err = snd_create_std_mono_ctl(mixer, 4, 1, 0x0, USB_MIXER_INV_BOOLEAN, +				"Headphone Playback Switch", NULL); +	if (err < 0) +		return err; +	err = snd_create_std_mono_ctl(mixer, 4, 2, 0x1, USB_MIXER_S16, +				"Headphone A Mix Playback Volume", NULL); +	if (err < 0) +		return err; +	err = snd_create_std_mono_ctl(mixer, 4, 2, 0x2, USB_MIXER_S16, +				"Headphone B Mix Playback Volume", NULL); +	if (err < 0) +		return err; + +	err = snd_create_std_mono_ctl(mixer, 7, 1, 0x0, USB_MIXER_INV_BOOLEAN, +				"Output Playback Switch", NULL); +	if (err < 0) +		return err; +	err = snd_create_std_mono_ctl(mixer, 7, 2, 0x1, USB_MIXER_S16, +				"Output A Playback Volume", NULL); +	if (err < 0) +		return err; +	err = snd_create_std_mono_ctl(mixer, 7, 2, 0x2, USB_MIXER_S16, +				"Output B Playback Volume", NULL); +	if (err < 0) +		return err; + +	err = snd_create_std_mono_ctl(mixer, 10, 1, 0x0, USB_MIXER_INV_BOOLEAN, +				"Input Capture Switch", NULL); +	if (err < 0) +		return err; +	err = snd_create_std_mono_ctl(mixer, 10, 2, 0x1, USB_MIXER_S16, +				"Input A Capture Volume", NULL); +	if (err < 0) +		return err; +	err = snd_create_std_mono_ctl(mixer, 10, 2, 0x2, USB_MIXER_S16, +				"Input B Capture Volume", NULL); +	if (err < 0) +		return err; + +	return 0; +} +  void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,  			       unsigned char samplerate_id)  { @@ -600,7 +1014,7 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)  	case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra */  	case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */ -		err = snd_maudio_ftu_create_mixer(mixer); +		err = snd_ftu_create_mixer(mixer);  		break;  	case USB_ID(0x0b05, 0x1739): @@ -619,6 +1033,10 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)  				snd_nativeinstruments_ta10_mixers,  				ARRAY_SIZE(snd_nativeinstruments_ta10_mixers));  		break; + +	case USB_ID(0x200c, 0x1018): /* Electrix Ebox-44 */ +		err = snd_ebox44_create_mixer(mixer); +		break;  	}  	return err;  |