Commit 7d2b451e authored by Sergiy Kovalchuk's avatar Sergiy Kovalchuk Committed by Takashi Iwai
Browse files

ALSA: usb-audio - Added functionality for E-mu 0404USB/0202USB/TrackerPre



Added functionality:
1) Extension Units support (all XU settings now available at alsamixer,
   kmix, etc):
- "AnalogueIn soft limiter" switch;
- "Sample rate" selector (values 0,1,2,3,4,5 corresponds to 44.1 48 ...
  192 kHz);
- "DigitalIn CLK source" selector (internal/external) (**);
- "DigitalOut format SPDIF/AC3" switch (**);
(**)E-mu-0404usb only.

2) Automatic device sample rate adjustment depending on substream
   samplerate for both capture and playback substream.

[minor coding-style fixes by tiwai]

Signed-off-by: default avatarSergiy Kovalchuk <cnb_zerg@yahoo.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 44eba3e8
Loading
Loading
Loading
Loading
+49 −0
Original line number Diff line number Diff line
@@ -1270,6 +1270,47 @@ static int init_usb_sample_rate(struct usb_device *dev, int iface,
	return 0;
}

/*
 * For E-Mu 0404USB/0202USB/TrackerPre sample rate should be set for device,
 * not for interface.
 */
static void set_format_emu_quirk(struct snd_usb_substream *subs,
				 struct audioformat *fmt)
{
	unsigned char emu_samplerate_id = 0;

	/* When capture is active
	 * sample rate shouldn't be changed
	 * by playback substream
	 */
	if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
		if (subs->stream->substream[SNDRV_PCM_STREAM_CAPTURE].interface != -1)
			return;
	}

	switch (fmt->rate_min) {
	case 48000:
		emu_samplerate_id = EMU_QUIRK_SR_48000HZ;
		break;
	case 88200:
		emu_samplerate_id = EMU_QUIRK_SR_88200HZ;
		break;
	case 96000:
		emu_samplerate_id = EMU_QUIRK_SR_96000HZ;
		break;
	case 176400:
		emu_samplerate_id = EMU_QUIRK_SR_176400HZ;
		break;
	case 192000:
		emu_samplerate_id = EMU_QUIRK_SR_192000HZ;
		break;
	default:
		emu_samplerate_id = EMU_QUIRK_SR_44100HZ;
		break;
	}
	snd_emuusb_set_samplerate(subs->stream->chip, emu_samplerate_id);
}

/*
 * find a matching format and set up the interface
 */
@@ -1383,6 +1424,14 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)

	subs->cur_audiofmt = fmt;

	switch (subs->stream->chip->usb_id) {
	case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */
	case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */
	case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */
		set_format_emu_quirk(subs, fmt);
		break;
	}

#if 0
	printk(KERN_DEBUG
	       "setting done: format = %d, rate = %d..%d, channels = %d\n",
+13 −0
Original line number Diff line number Diff line
@@ -208,6 +208,16 @@ struct snd_usb_midi_endpoint_info {
/*
 */

/*E-mu USB samplerate control quirk*/
enum {
	EMU_QUIRK_SR_44100HZ = 0,
	EMU_QUIRK_SR_48000HZ,
	EMU_QUIRK_SR_88200HZ,
	EMU_QUIRK_SR_96000HZ,
	EMU_QUIRK_SR_176400HZ,
	EMU_QUIRK_SR_192000HZ
};

#define combine_word(s)    ((*(s)) | ((unsigned int)(s)[1] << 8))
#define combine_triple(s)  (combine_word(s) | ((unsigned int)(s)[2] << 16))
#define combine_quad(s)    (combine_triple(s) | ((unsigned int)(s)[3] << 24))
@@ -233,6 +243,9 @@ void snd_usbmidi_input_stop(struct list_head* p);
void snd_usbmidi_input_start(struct list_head* p);
void snd_usbmidi_disconnect(struct list_head *p);

void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
			unsigned char samplerate_id);

/*
 * retrieve usb_interface descriptor from the host interface
 * (conditional for compatibility with the older API)
+71 −4
Original line number Diff line number Diff line
@@ -186,6 +186,21 @@ enum {
	USB_PROC_DCR_RELEASE = 6,
};

/*E-mu 0202(0404) eXtension Unit(XU) control*/
enum {
	USB_XU_CLOCK_RATE 		= 0xe301,
	USB_XU_CLOCK_SOURCE		= 0xe302,
	USB_XU_DIGITAL_IO_STATUS	= 0xe303,
	USB_XU_DEVICE_OPTIONS		= 0xe304,
	USB_XU_DIRECT_MONITORING	= 0xe305,
	USB_XU_METERING			= 0xe306
};
enum {
	USB_XU_CLOCK_SOURCE_SELECTOR = 0x02,	/* clock source*/
	USB_XU_CLOCK_RATE_SELECTOR = 0x03,	/* clock rate */
	USB_XU_DIGITAL_FORMAT_SELECTOR = 0x01,	/* the spdif format */
	USB_XU_SOFT_LIMIT_SELECTOR = 0x03	/* soft limiter */
};

/*
 * manual mapping of mixer names
@@ -1330,7 +1345,32 @@ static struct procunit_info procunits[] = {
	{ USB_PROC_DCR, "DCR", dcr_proc_info },
	{ 0 },
};

/*
 * predefined data for extension units
 */
static struct procunit_value_info clock_rate_xu_info[] = {
       { USB_XU_CLOCK_RATE_SELECTOR, "Selector", USB_MIXER_U8, 0 },
       { 0 }
};
static struct procunit_value_info clock_source_xu_info[] = {
	{ USB_XU_CLOCK_SOURCE_SELECTOR, "External", USB_MIXER_BOOLEAN },
	{ 0 }
};
static struct procunit_value_info spdif_format_xu_info[] = {
	{ USB_XU_DIGITAL_FORMAT_SELECTOR, "SPDIF/AC3", USB_MIXER_BOOLEAN },
	{ 0 }
};
static struct procunit_value_info soft_limit_xu_info[] = {
	{ USB_XU_SOFT_LIMIT_SELECTOR, " ", USB_MIXER_BOOLEAN },
	{ 0 }
};
static struct procunit_info extunits[] = {
	{ USB_XU_CLOCK_RATE, "Clock rate", clock_rate_xu_info },
	{ USB_XU_CLOCK_SOURCE, "DigitalIn CLK source", clock_source_xu_info },
	{ USB_XU_DIGITAL_IO_STATUS, "DigitalOut format:", spdif_format_xu_info },
	{ USB_XU_DEVICE_OPTIONS, "AnalogueIn Soft Limit", soft_limit_xu_info },
	{ 0 }
};
/*
 * build a processing/extension unit
 */
@@ -1391,8 +1431,18 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
			cval->max = dsc[15];
			cval->res = 1;
			cval->initialized = 1;
		} else {
			if (type == USB_XU_CLOCK_RATE) {
				/* E-Mu USB 0404/0202/TrackerPre
				 * samplerate control quirk
				 */
				cval->min = 0;
				cval->max = 5;
				cval->res = 1;
				cval->initialized = 1;
			} else
				get_min_max(cval, valinfo->min_value);
		}

		kctl = snd_ctl_new1(&mixer_procunit_ctl, cval);
		if (! kctl) {
@@ -1433,7 +1483,7 @@ static int parse_audio_processing_unit(struct mixer_build *state, int unitid, un

static int parse_audio_extension_unit(struct mixer_build *state, int unitid, unsigned char *desc)
{
	return build_audio_procunit(state, unitid, desc, NULL, "Extension Unit");
	return build_audio_procunit(state, unitid, desc, extunits, "Extension Unit");
}


@@ -2109,6 +2159,23 @@ static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer)
	return 0;
}

void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
		unsigned char samplerate_id)
{
	 struct usb_mixer_interface *mixer;
	 struct usb_mixer_elem_info *cval;
	 int unitid = 12; /* SamleRate ExtensionUnit ID */

	 list_for_each_entry(mixer, &chip->mixer_list, list) {
		 cval = mixer->id_elems[unitid];
		 if (cval) {
			set_cur_ctl_value(cval, cval->control << 8, samplerate_id);
			snd_usb_mixer_notify_id(mixer, unitid);
		 }
		 break;
	 }
}

int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
			 int ignore_error)
{