⛏️ index : haiku.git

/*
 * Copyright 2004-2015 Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Jérôme Duval, jerome.duval@free.fr
 *		Marcus Overhagen, marcus@overhagen.de
 *		Jérôme Lévêque, leveque.jerome@gmail.com
 */


#include "ice1712_reg.h"
#include "io.h"
#include "multi.h"
#include "util.h"

#include <string.h>
#include "debug.h"

status_t ice1712Settings_apply(ice1712 *card);

static void ice1712Buffer_Start(ice1712 *card);
static uint32 ice1712UI_GetCombo(ice1712 *card, uint32 index);
static void ice1712UI_SetCombo(ice1712 *card, uint32 index, uint32 value);
static uint32 ice1712UI_GetOutput(ice1712 *card, uint32 index);
static void ice1712UI_SetOutput(ice1712 *card, uint32 index, uint32 value);
static void ice1712UI_GetVolume(ice1712 *card, multi_mix_value *mmv);
static void ice1712UI_SetVolume(ice1712 *card, multi_mix_value *mmv);
static void ice1712UI_CreateOutput(ice1712 *card, multi_mix_control **p_mmc,
	int32 output, int32 parent);
static void ice1712UI_CreateCombo(multi_mix_control **p_mmc,
	const char *values[], int32 parent, int32 nb_combo, const char *name);
static void ice1712UI_CreateChannel(multi_mix_control **p_mmc,
	int32 channel, int32 parent, const char* name);
static int32 ice1712UI_CreateGroup(multi_mix_control **p_mmc,
	int32 index, int32 parent, enum strind_id string, const char* name);
static int32 nb_control_created;

#define AUTHORIZED_RATE (B_SR_SAME_AS_INPUT | B_SR_96000 \
	| B_SR_88200 | B_SR_48000 | B_SR_44100)
#define AUTHORIZED_SAMPLE_SIZE (B_FMT_24BIT)

#define MAX_CONTROL	32

//ICE1712 Multi - Buffer
//----------------------

void
ice1712Buffer_Start(ice1712 *card)
{
	uint16 size = card->buffer_size * MAX_DAC;

	write_mt_uint8(card, MT_PROF_PB_CONTROL, 0);

	write_mt_uint32(card, MT_PROF_PB_DMA_BASE_ADDRESS,
		(uint32)(card->phys_pb.address));
	write_mt_uint16(card, MT_PROF_PB_DMA_COUNT_ADDRESS,
		(size * SWAPPING_BUFFERS) - 1);
	//We want interrupt only from playback
	write_mt_uint16(card, MT_PROF_PB_DMA_TERM_COUNT, size - 1);
	ITRACE("SIZE DMA PLAYBACK %#x\n", size);

	size = card->buffer_size * MAX_ADC;

	write_mt_uint32(card, MT_PROF_REC_DMA_BASE_ADDRESS,
		(uint32)(card->phys_rec.address));
	write_mt_uint16(card, MT_PROF_REC_DMA_COUNT_ADDRESS,
		(size * SWAPPING_BUFFERS) - 1);
	//We do not want any interrupt from the record
	write_mt_uint16(card, MT_PROF_REC_DMA_TERM_COUNT, 0);
	ITRACE("SIZE DMA RECORD %#x\n", size);

	//Enable output AND Input from Analog CODEC
	switch (card->config.product) {
	//TODO: find correct value for all card
		case ICE1712_SUBDEVICE_DELTA66:
		case ICE1712_SUBDEVICE_DELTA44:
		case ICE1712_SUBDEVICE_AUDIOPHILE_2496:
		case ICE1712_SUBDEVICE_DELTADIO2496:
		case ICE1712_SUBDEVICE_DELTA410:
		case ICE1712_SUBDEVICE_DELTA1010LT:
		case ICE1712_SUBDEVICE_DELTA1010:
			codec_write(card, AK45xx_CLOCK_FORMAT_REGISTER, 0x69);
			codec_write(card, AK45xx_RESET_REGISTER, 0x03);
			break;
		case ICE1712_SUBDEVICE_VX442:
//			ak45xx_write_gpio(ice, reg_addr, data, VX442_CODEC_CS_0);
//			ak45xx_write_gpio(ice, reg_addr, data, VX442_CODEC_CS_1);
			break;
	}

	//Set Data Format for SPDif codec
	switch (card->config.product) {
	//TODO: find correct value for all card
		case ICE1712_SUBDEVICE_DELTA1010:
			break;
		case ICE1712_SUBDEVICE_DELTADIO2496:
			break;
		case ICE1712_SUBDEVICE_DELTA66:
		case ICE1712_SUBDEVICE_DELTA44:
//			ak45xx_write_gpio(ice, reg_addr, data, DELTA66_CODEC_CS_0);
//			ak45xx_write_gpio(ice, reg_addr, data, DELTA66_CODEC_CS_1);
			break;
		case ICE1712_SUBDEVICE_AUDIOPHILE_2496:
			spdif_write(card, CS84xx_SERIAL_INPUT_FORMAT_REG, 0x85);
			spdif_write(card, CS84xx_SERIAL_OUTPUT_FORMAT_REG, 0x85);
//			spdif_write(card, CS84xx_SERIAL_OUTPUT_FORMAT_REG, 0x41);
			break;
		case ICE1712_SUBDEVICE_DELTA410:
			break;
		case ICE1712_SUBDEVICE_DELTA1010LT:
//			ak45xx_write_gpio(ice, reg_addr, data,
//				DELTA1010LT_CODEC_CS_0);
//			ak45xx_write_gpio(ice, reg_addr, data,
//				DELTA1010LT_CODEC_CS_1);
//			ak45xx_write_gpio(ice, reg_addr, data,
//				DELTA1010LT_CODEC_CS_2);
//			ak45xx_write_gpio(ice, reg_addr, data,
//				DELTA1010LT_CODEC_CS_3);
			break;
		case ICE1712_SUBDEVICE_VX442:
//			ak45xx_write_gpio(ice, reg_addr, data, VX442_CODEC_CS_0);
//			ak45xx_write_gpio(ice, reg_addr, data, VX442_CODEC_CS_1);
			break;
	}

	card->buffer = 1;
	write_mt_uint8(card, MT_PROF_PB_CONTROL, 5);
}


status_t
ice1712Buffer_Exchange(ice1712 *card, multi_buffer_info *data)
{
	multi_buffer_info buffer_info;

#ifdef __HAIKU__
	if (user_memcpy(&buffer_info, data, sizeof(buffer_info)) < B_OK)
		return B_BAD_ADDRESS;
#else
	memcpy(&buffer_info, data, sizeof(buffer_info));
#endif

	buffer_info.flags = B_MULTI_BUFFER_PLAYBACK | B_MULTI_BUFFER_RECORD;

	if (acquire_sem_etc(card->buffer_ready_sem, 1, B_RELATIVE_TIMEOUT
		| B_CAN_INTERRUPT, 50000) == B_TIMED_OUT) {
		ITRACE("buffer_exchange timeout\n");
	};

	// Playback buffers info
	buffer_info.played_real_time = card->played_time;
	buffer_info.played_frames_count = card->frames_count;
	buffer_info.playback_buffer_cycle = (card->buffer - 1)
		% SWAPPING_BUFFERS; //Buffer played

	// Record buffers info
	buffer_info.recorded_real_time = card->played_time;
	buffer_info.recorded_frames_count = card->frames_count;
	buffer_info.record_buffer_cycle = (card->buffer - 1)
		% SWAPPING_BUFFERS; //Buffer filled

#ifdef __HAIKU__
	if (user_memcpy(data, &buffer_info, sizeof(buffer_info)) < B_OK)
		return B_BAD_ADDRESS;
#else
	memcpy(data, &buffer_info, sizeof(buffer_info));
#endif

	return B_OK;
}


status_t
ice1712Buffer_Stop(ice1712 *card)
{
	write_mt_uint8(card, MT_PROF_PB_CONTROL, 0);

	card->played_time = 0;
	card->frames_count = 0;
	card->buffer = 0;

	return B_OK;
}

//ICE1712 Multi - Description
//---------------------------

status_t
ice1712Get_Description(ice1712 *card, multi_description *data)
{
	int chan = 0, i, size;

	data->interface_version = B_CURRENT_INTERFACE_VERSION;
	data->interface_minimum = B_CURRENT_INTERFACE_VERSION;

	switch (card->config.product) {
		case ICE1712_SUBDEVICE_DELTA1010:
			strncpy(data->friendly_name, "Delta 1010", 32);
			break;
		case ICE1712_SUBDEVICE_DELTADIO2496:
			strncpy(data->friendly_name, "Delta DIO 2496", 32);
			break;
		case ICE1712_SUBDEVICE_DELTA66:
			strncpy(data->friendly_name, "Delta 66", 32);
			break;
		case ICE1712_SUBDEVICE_DELTA44:
			strncpy(data->friendly_name, "Delta 44", 32);
			break;
		case ICE1712_SUBDEVICE_AUDIOPHILE_2496:
			strncpy(data->friendly_name, "Audiophile 2496", 32);
			break;
		case ICE1712_SUBDEVICE_DELTA410:
			strncpy(data->friendly_name, "Delta 410", 32);
			break;
		case ICE1712_SUBDEVICE_DELTA1010LT:
			strncpy(data->friendly_name, "Delta 1010 LT", 32);
			break;
		case ICE1712_SUBDEVICE_VX442:
			strncpy(data->friendly_name, "VX 442", 32);
			break;

		default:
			strncpy(data->friendly_name, "Unknow device", 32);
			break;
	}

	strncpy(data->vendor_info, "Haiku", 32);

	data->output_channel_count = card->total_output_channels;
	data->input_channel_count = card->total_input_channels;
	data->output_bus_channel_count = 0;
	data->input_bus_channel_count = 0;
	data->aux_bus_channel_count = 0;

	size =	data->output_channel_count + data->input_channel_count
		+ data->output_bus_channel_count + data->input_bus_channel_count
		+ data->aux_bus_channel_count;

	ITRACE_VV("request_channel_count = %" B_PRIi32 "\n",
		data->request_channel_count);

	if (size <= data->request_channel_count) {
		for (i = 0; i < card->config.nb_DAC; i++) {
		//Analog STEREO output
			data->channels[chan].channel_id = chan;
			data->channels[chan].kind = B_MULTI_OUTPUT_CHANNEL;
			data->channels[chan].designations = B_CHANNEL_STEREO_BUS
				| (((i & 1) == 0) ? B_CHANNEL_LEFT : B_CHANNEL_RIGHT);
			data->channels[chan].connectors = 0;
			chan++;
		}

		if (card->config.spdif & SPDIF_OUT_PRESENT) {
		//SPDIF STEREO output
			data->channels[chan].channel_id = chan;
			data->channels[chan].kind = B_MULTI_OUTPUT_CHANNEL;
			data->channels[chan].designations = B_CHANNEL_STEREO_BUS
				| B_CHANNEL_LEFT;
			data->channels[chan].connectors = 0;
			chan++;
			data->channels[chan].channel_id = chan;
			data->channels[chan].kind = B_MULTI_OUTPUT_CHANNEL;
			data->channels[chan].designations = B_CHANNEL_STEREO_BUS
				| B_CHANNEL_RIGHT;
			data->channels[chan].connectors = 0;
			chan++;
		}

		for (i = 0; i < card->config.nb_ADC; i++) {
		//Analog STEREO input
			data->channels[chan].channel_id = chan;
			data->channels[chan].kind = B_MULTI_INPUT_CHANNEL;
			data->channels[chan].designations = B_CHANNEL_STEREO_BUS
				| (((i & 1) == 0) ? B_CHANNEL_LEFT : B_CHANNEL_RIGHT);
			data->channels[chan].connectors = 0;
			chan++;
		}

		if (card->config.spdif & SPDIF_IN_PRESENT) {
		//SPDIF STEREO input
			data->channels[chan].channel_id = chan;
			data->channels[chan].kind = B_MULTI_INPUT_CHANNEL;
			data->channels[chan].designations = B_CHANNEL_STEREO_BUS
				| B_CHANNEL_LEFT;
			data->channels[chan].connectors = 0;
			chan++;
			data->channels[chan].channel_id = chan;
			data->channels[chan].kind = B_MULTI_INPUT_CHANNEL;
			data->channels[chan].designations = B_CHANNEL_STEREO_BUS
				| B_CHANNEL_RIGHT;
			data->channels[chan].connectors = 0;
			chan++;
		}

		//The digital mixer output (it's an Input for Haiku)
		data->channels[chan].channel_id = chan;
		data->channels[chan].kind = B_MULTI_INPUT_CHANNEL;
		data->channels[chan].designations = B_CHANNEL_STEREO_BUS
			| B_CHANNEL_LEFT;
		data->channels[chan].connectors = 0;
		chan++;
		data->channels[chan].channel_id = chan;
		data->channels[chan].kind = B_MULTI_INPUT_CHANNEL;
		data->channels[chan].designations = B_CHANNEL_STEREO_BUS
			| B_CHANNEL_RIGHT;
		data->channels[chan].connectors = 0;
		chan++;
	}

	ITRACE("output_channel_count = %" B_PRIi32 "\n",
		data->output_channel_count);
	ITRACE("input_channel_count = %" B_PRIi32 "\n",
		data->input_channel_count);
	ITRACE("output_bus_channel_count = %" B_PRIi32 "\n",
		data->output_bus_channel_count);
	ITRACE("input_bus_channel_count = %" B_PRIi32 "\n",
		data->input_bus_channel_count);

	data->output_rates = data->input_rates = AUTHORIZED_RATE;
	data->min_cvsr_rate = 44100;
	data->max_cvsr_rate = 96000;

	data->output_formats = data->input_formats = AUTHORIZED_SAMPLE_SIZE;
	data->lock_sources = B_MULTI_LOCK_INTERNAL | B_MULTI_LOCK_SPDIF;
	data->timecode_sources = 0;
	data->interface_flags = B_MULTI_INTERFACE_PLAYBACK
		| B_MULTI_INTERFACE_RECORD;
	data->start_latency = 0;

	strcpy(data->control_panel,"");

	return B_OK;
}


status_t
ice1712Get_Channel(ice1712 *card, multi_channel_enable *data)
{
	int i, total_channel;
	uint8 reg;

	total_channel = card->total_output_channels + card->total_input_channels;
	for (i = 0; i < total_channel; i++)
		B_SET_CHANNEL(data->enable_bits, i, true);

	reg = read_mt_uint8(card, MT_SAMPLING_RATE_SELECT);

	if (reg == 0x10)
		data->lock_source = B_MULTI_LOCK_SPDIF;
	else
		data->lock_source = B_MULTI_LOCK_INTERNAL;

	return B_OK;
}


status_t
ice1712Set_Channel(ice1712 *card, multi_channel_enable *data)
{
	int i;
	int total_channel;

	total_channel = card->total_output_channels + card->total_input_channels;
	for (i = 0; i < total_channel; i++)
		ITRACE_VV("set_enabled_channels %d : %s\n", i,
			B_TEST_CHANNEL(data->enable_bits, i) ? "enabled": "disabled");

	ITRACE_VV("lock_source %" B_PRIx32 "\n", data->lock_source);
	ITRACE_VV("lock_data %" B_PRIx32 "\n", data->lock_data);

	if (data->lock_source == B_MULTI_LOCK_SPDIF)
		write_mt_uint8(card, MT_SAMPLING_RATE_SELECT, 0x10);
	else
		write_mt_uint8(card, MT_SAMPLING_RATE_SELECT,
			card->config.samplingRate);

	card->config.lockSource = data->lock_source;

	return B_OK;
}


status_t
ice1712Get_Format(ice1712 *card, multi_format_info *data)
{
	uint8 sr = read_mt_uint8(card, MT_SAMPLING_RATE_SELECT);

	switch (sr) {
		case ICE1712_SAMPLERATE_48K:
			data->input.rate = data->output.rate = B_SR_48000;
			data->input.cvsr = data->output.cvsr = 48000.0f;
			break;
		case ICE1712_SAMPLERATE_96K:
			data->input.rate = data->output.rate = B_SR_96000;
			data->input.cvsr = data->output.cvsr = 96000.0f;
			break;
		case ICE1712_SAMPLERATE_44K1:
			data->input.rate = data->output.rate = B_SR_44100;
			data->input.cvsr = data->output.cvsr = 44100.0f;
			break;
		case ICE1712_SAMPLERATE_88K2:
			data->input.rate = data->output.rate = B_SR_88200;
			data->input.cvsr = data->output.cvsr = 88200.0f;
			break;
	}

	data->timecode_kind = 0;
	data->output_latency = data->input_latency = 0;
	data->output.format = data->input.format = AUTHORIZED_SAMPLE_SIZE;

	ITRACE("Sampling Rate = %f\n", data->input.cvsr);

	return B_OK;
}


status_t
ice1712Set_Format(ice1712 *card, multi_format_info *data)
{
	ITRACE("Input Sampling Rate = %" B_PRIu32 "\n",
		data->input.rate);
	ITRACE("Output Sampling Rate = %" B_PRIu32 "\n",
		data->output.rate);

	//We can't have a different rate for input and output
	//so just wait to change our sample rate when
	//media server will do what we need
	//Lie to it and say we are living in wonderland
	if (data->input.rate != data->output.rate)
		return B_OK;

	if (card->config.lockSource == B_MULTI_LOCK_INTERNAL) {
		switch (data->output.rate) {
			case B_SR_96000:
				card->config.samplingRate = 0x07;
				break;
			case B_SR_88200:
				card->config.samplingRate = 0x0B;
				break;
			case B_SR_48000:
				card->config.samplingRate = 0x00;
				break;
			case B_SR_44100:
				card->config.samplingRate = 0x08;
				break;
		}
		write_mt_uint8(card, MT_SAMPLING_RATE_SELECT,
			card->config.samplingRate);
	}
	ITRACE("New rate = %#x\n", read_mt_uint8(card, MT_SAMPLING_RATE_SELECT));

	return B_OK;
}

//ICE1712 Multi - UI
//------------------

static const char *Clock[] = {
	"Internal",
	"Digital",
	NULL,
};

static const char *DigitalFormat[] = {
	"Consumer",
	"Professional",
	NULL,
};

static const char *DigitalEmphasis[] = {
	"None",
	"CCITT",
	"15/50usec",
	NULL,
};

static const char *DigitalCopyMode[] = {
	"Original",
	"1st Generation",
	"No SCMS",
	NULL,
};

static const char **SettingsGeneral[] = {
	Clock,
	NULL,
};

static const char **SettingsDigital[] = {
	DigitalFormat,
	DigitalEmphasis,
	DigitalCopyMode,
	NULL,
};

static const char *string_list[] = {
	"Setup",
	"General",
	"Digital",
	"Output Selection",

	"Internal Mixer",

	//General settings
	"Master clock",
	"reserved_0",
	"reserved_1",

	//Digital settings
	"Output format",
	"Emphasis",
	"Copy mode",

	//Output Selection
	"Output 1", //11
	"Output 2",
	"Output 3",
	"Output 4",
	"Digital Output", //15

	"Haiku output", //16

	"Input 1", //17
	"Input 2",
	"Input 3",
	"Input 4",
	"Digital Input", //21
	"Internal mixer", //22
};


/*
 * This will create a Tab
 */
int32
ice1712UI_CreateGroup(multi_mix_control **p_mmc, int32 index,
	int32 parent, enum strind_id string, const char* name)
{
	multi_mix_control *mmc = *p_mmc;
	int32 group;

	mmc->id = ICE1712_MULTI_CONTROL_FIRSTID + ICE1712_MULTI_SET_INDEX(index);
	mmc->parent = parent;
	mmc->flags = B_MULTI_MIX_GROUP;
	mmc->master = CONTROL_IS_MASTER;
	mmc->string = string;

	group = mmc->id;

	if (name != NULL)
		strcpy(mmc->name, name);

	ITRACE_VV("Create Group: ID %#" B_PRIx32 "\n", mmc->id);

	nb_control_created++; mmc++;
	(*p_mmc) = mmc;

	return group;
}


/*
 * This will create a Slider with a "Mute" CheckBox
 */
void
ice1712UI_CreateChannel(multi_mix_control **p_mmc, int32 channel,
	int32 parent, const char* name)
{
	int32 id = ICE1712_MULTI_CONTROL_FIRSTID
		+ ICE1712_MULTI_CONTROL_TYPE_VOLUME
		+ ICE1712_MULTI_SET_CHANNEL(channel);
	multi_mix_control *mmc = *p_mmc;
	multi_mix_control control;

	control.master = CONTROL_IS_MASTER;
	control.parent = parent;
	control.gain.max_gain = 0.0;
	control.gain.min_gain = -144.0;
	control.gain.granularity = 1.5;

	//The Mute Checkbox
	control.id = id++;
	control.flags = B_MULTI_MIX_ENABLE;
	control.string = S_MUTE;
	*mmc = control;
	mmc++;

	ITRACE_VV("Create Channel (Mute): ID %#" B_PRIx32 "\n", control.id);

	//The Left Slider
	control.string = S_null;
	control.id = id++;
	control.flags = B_MULTI_MIX_GAIN;
	if (name != NULL)
		strcpy(control.name, name);
	*mmc = control;
	mmc++;

	ITRACE_VV("Create Channel (Left): ID %#" B_PRIx32 "\n", control.id);

	//The Right Slider
	control.master = control.id; //The Id of the Left Slider
	control.id = id++;
	*mmc = control;
	mmc++;

	ITRACE_VV("Create Channel (Right): ID %#" B_PRIx32 "\n", control.id);

	nb_control_created += 3;
	(*p_mmc) = mmc;
}


void
ice1712UI_CreateCombo(multi_mix_control **p_mmc, const char *values[],
	int32 parent, int32 nb_combo, const char *name)
{
	int32 id = ICE1712_MULTI_CONTROL_FIRSTID
		+ ICE1712_MULTI_CONTROL_TYPE_COMBO
		+ ICE1712_MULTI_SET_CHANNEL(nb_combo);
	multi_mix_control *mmc = *p_mmc;
	int32 parentControl, i;

	//The label
	parentControl = mmc->id = id++;
	mmc->flags = B_MULTI_MIX_MUX;
	mmc->parent = parent;
	strcpy(mmc->name, name);

	ITRACE_VV("Create Combo (label): ID %#" B_PRIx32 "\n", parentControl);

	nb_control_created++; mmc++;

	//The values
	for (i = 0; values[i] != NULL; i++) {
		mmc->id = id++;
		mmc->flags = B_MULTI_MIX_MUX_VALUE;
		mmc->parent = parentControl;
		strcpy(mmc->name, values[i]);

		ITRACE_VV("Create Combo (value): ID %#" B_PRIx32 "\n", mmc->id);

		nb_control_created++; mmc++;
	}

	(*p_mmc) = mmc;
}


/*
 * This will create all possible value for the output
 * output 0 -> 3 (physical stereo output) 4 is the Digital
 */
void
ice1712UI_CreateOutput(ice1712 *card, multi_mix_control **p_mmc,
	int32 output, int32 parent)
{
	int32 id = ICE1712_MULTI_CONTROL_FIRSTID
		+ ICE1712_MULTI_CONTROL_TYPE_OUTPUT
		+ ICE1712_MULTI_SET_CHANNEL(output);
	multi_mix_control *mmc = *p_mmc;
	int32 parentControl, i;

	//The label
	parentControl = mmc->id = id++;
	mmc->flags = B_MULTI_MIX_MUX;
	mmc->parent = parent;
	strcpy(mmc->name, string_list[11 + output]);
	nb_control_created++; mmc++;

	ITRACE_VV("Create Output (label): ID %#" B_PRIx32 "\n", parentControl);

	//Haiku output
	mmc->id = id++;
	mmc->flags = B_MULTI_MIX_MUX_VALUE;
	mmc->parent = parentControl;
	strcpy(mmc->name, string_list[16]);

	ITRACE_VV("Create Output (Haiku): ID %#" B_PRIx32 "\n", mmc->id);

	nb_control_created++; mmc++;

	//Physical Input
	for (i = 0; i < card->config.nb_DAC; i += 2) {
		mmc->id = id++;
		mmc->flags = B_MULTI_MIX_MUX_VALUE;
		mmc->parent = parentControl;
		strcpy(mmc->name, string_list[17 + (i / 2)]);

		ITRACE_VV("Create Output (Physical In): ID %#" B_PRIx32 "\n", mmc->id);

		nb_control_created++; mmc++;
	}

	//Physical Digital Input
	if (card->config.spdif & SPDIF_IN_PRESENT) {
		mmc->id = id++;
		mmc->flags = B_MULTI_MIX_MUX_VALUE;
		mmc->parent = parentControl;
		strcpy(mmc->name, string_list[21]);

		ITRACE_VV("Create Output (Digital In) ID %#" B_PRIx32 "\n", mmc->id);

		nb_control_created++; mmc++;
	}

	//Internal mixer only for Output 1 and Digital Output
	if ((output == 0) || (output == 4)) {
		mmc->id = id++;
		mmc->flags = B_MULTI_MIX_MUX_VALUE;
		mmc->parent = parentControl;
		strcpy(mmc->name, string_list[22]);

		ITRACE_VV("Create Output (Mix); ID %#" B_PRIx32 "\n", mmc->id);

		nb_control_created++; mmc++;
	}

	(*p_mmc) = mmc;
}


uint32
ice1712UI_GetCombo(ice1712 *card, uint32 index)
{
	uint32 value = 0;

	switch (index) {
		case 0:
			value = card->settings.clock;
			break;

		case 1:
			value = card->settings.outFormat;
			break;

		case 2:
			value = card->settings.emphasis;
			break;

		case 3:
			value = card->settings.copyMode;
			break;
	}

	ITRACE_VV("Get combo: %" B_PRIu32 ", %" B_PRIu32 "\n",
		index, value);

	return value;
}


void
ice1712UI_SetCombo(ice1712 *card, uint32 index, uint32 value)
{
	ITRACE_VV("Set combo: %" B_PRIu32 ", %" B_PRIu32 "\n", index, value);

	switch (index) {
		case 0:
			if (value < 2)
				card->settings.clock = value;
			break;

		case 1:
			if (value < 2)
				card->settings.outFormat = value;
			break;

		case 2:
			if (value < 3)
				card->settings.emphasis = value;
			break;

		case 3:
			if (value < 3)
				card->settings.copyMode = value;
			break;
	}
}


uint32
ice1712UI_GetOutput(ice1712 *card, uint32 index)
{
	uint32 value = 0;

	if (index < 5)
		value = card->settings.output[index];

	ITRACE_VV("Get output: %" B_PRIu32 ", %" B_PRIu32 "\n", index, value);

	return value;
}


void
ice1712UI_SetOutput(ice1712 *card, uint32 index, uint32 value)
{
	if (index < 5)
		card->settings.output[index] = value;

	ITRACE_VV("Set output: %" B_PRIu32 ", %" B_PRIu32 "\n", index, value);
}


void
ice1712UI_GetVolume(ice1712 *card, multi_mix_value *mmv)
{
	ice1712Volume *vol;
	uint32 chan = ICE1712_MULTI_GET_CHANNEL(mmv->id);

	ITRACE_VV("Get volume\n");

	if (chan < ICE1712_HARDWARE_VOLUME) {
		vol = card->settings.playback;
	} else {
		vol = card->settings.record;
		chan -= ICE1712_HARDWARE_VOLUME;
	}

	//chan is normaly <= ICE1712_HARDWARE_VOLUME
	switch (ICE1712_MULTI_GET_INDEX(mmv->id)) {
		case 0: //Mute
			mmv->enable = vol[chan].mute | vol[chan + 1].mute;
			ITRACE_VV("	Get mute %d for channel %d or %d\n",
				mmv->enable, (int)chan, (int)chan + 1);
			break;

		case 2: //Right channel
			chan++;
			//No break
		case 1: //Left channel
			mmv->gain = vol[chan].volume;
			ITRACE_VV("	Get Volume %f for channel %d\n",
				mmv->gain, (int)chan);
			break;
	}
}


void
ice1712UI_SetVolume(ice1712 *card, multi_mix_value *mmv)
{
	ice1712Volume *vol;
	uint32 chan = ICE1712_MULTI_GET_CHANNEL(mmv->id);

	ITRACE_VV("Set volume\n");

	if (chan < ICE1712_HARDWARE_VOLUME) {
		vol = card->settings.playback;
	} else {
		vol = card->settings.record;
		chan -= ICE1712_HARDWARE_VOLUME;
	}

	//chan is normaly <= ICE1712_HARDWARE_VOLUME
	switch (ICE1712_MULTI_GET_INDEX(mmv->id)) {
		case 0: //Mute
			vol[chan].mute = mmv->enable;
			vol[chan + 1].mute = mmv->enable;
			ITRACE_VV("	Change mute to %d for channel %d and %d\n",
				mmv->enable, (int)chan, (int)chan + 1);
			break;

		case 2: //Right channel
			chan++;
			//No break
		case 1: //Left channel
			vol[chan].volume = mmv->gain;
			ITRACE_VV("	Change Volume to %f for channel %d\n",
				mmv->gain, (int)chan);
			break;
	}
}

//ICE1712 Multi - MIX
//-------------------

status_t
ice1712Get_MixValue(ice1712 *card, multi_mix_value_info *data)
{
	int i;

	for (i = 0; i < data->item_count; i++) {
		multi_mix_value *mmv = &(data->values[i]);
		ITRACE_VV("Get Mix: Id %" B_PRIu32 "\n", mmv->id);
		switch (mmv->id & ICE1712_MULTI_CONTROL_TYPE_MASK) {
			case ICE1712_MULTI_CONTROL_TYPE_COMBO:
				mmv->mux = ice1712UI_GetCombo(card,
					ICE1712_MULTI_GET_CHANNEL(mmv->id));
				break;

			case ICE1712_MULTI_CONTROL_TYPE_VOLUME:
				ice1712UI_GetVolume(card, mmv);
				break;

			case ICE1712_MULTI_CONTROL_TYPE_OUTPUT:
				mmv->mux = ice1712UI_GetOutput(card,
					ICE1712_MULTI_GET_CHANNEL(mmv->id));
				break;

			default:
				ITRACE_VV("Get Mix: unknow %" B_PRIu32 "\n", mmv->id);
				break;
		}
	}

	return B_OK;
}


status_t
ice1712Set_MixValue(ice1712 *card, multi_mix_value_info *data)
{
	int i;

	for (i = 0; i < data->item_count; i++) {
		multi_mix_value *mmv = &(data->values[i]);
		ITRACE_VV("Set Mix: Id %" B_PRIu32 "\n", mmv->id);
		switch (mmv->id & ICE1712_MULTI_CONTROL_TYPE_MASK) {
			case ICE1712_MULTI_CONTROL_TYPE_COMBO:
				ice1712UI_SetCombo(card,
					ICE1712_MULTI_GET_CHANNEL(mmv->id), mmv->mux);
				break;

			case ICE1712_MULTI_CONTROL_TYPE_VOLUME:
				ice1712UI_SetVolume(card, mmv);
				break;

			case ICE1712_MULTI_CONTROL_TYPE_OUTPUT:
				ice1712UI_SetOutput(card,
					ICE1712_MULTI_GET_CHANNEL(mmv->id), mmv->mux);
				break;

			default:
				ITRACE_VV("Set Mix: unknow %" B_PRIu32 "\n", mmv->id);
				break;
		}
	}

	return ice1712Settings_apply(card);
}


/*
 * Not implemented
 */
status_t
ice1712Get_MixValueChannel(ice1712 *card, multi_mix_channel_info *data)
{
	return B_OK;
}


status_t
ice1712Get_MixValueControls(ice1712 *card, multi_mix_control_info *mmci)
{
	int32 i;
	uint32 parentTab, parentTabColumn;
	multi_mix_control *mmc = mmci->controls;
	uint32 group = 0, combo = 0, channel = 0;

	nb_control_created = 0;

	ITRACE_VV("Get MixValue Channels: Max %" B_PRIi32 "\n", mmci->control_count);

	//Cleaning
	memset(mmc, 0, mmci->control_count * sizeof(multi_mix_control));

	//Setup tab
	parentTab = ice1712UI_CreateGroup(&mmc, group++,
		CONTROL_IS_MASTER, S_SETUP, NULL);

	//General Settings
	parentTabColumn = ice1712UI_CreateGroup(&mmc, group++, parentTab,
		S_null, string_list[1]);
	for (i = 0; SettingsGeneral[i] != NULL; i++) {
		ice1712UI_CreateCombo(&mmc, SettingsGeneral[i], parentTabColumn,
			combo++, string_list[5 + i]);
	}

	//Digital Settings
	parentTabColumn = ice1712UI_CreateGroup(&mmc, group++, parentTab,
		S_null, string_list[2]);
	for (i = 0; SettingsDigital[i] != NULL; i++) {
		ice1712UI_CreateCombo(&mmc, SettingsDigital[i], parentTabColumn,
			combo++, string_list[8 + i]);
	}

	//Output Selection Settings
	parentTabColumn = ice1712UI_CreateGroup(&mmc, group++, parentTab,
		S_null, string_list[3]);
	for (i = 0; i < card->config.nb_DAC; i += 2) {
		ice1712UI_CreateOutput(card, &mmc, i / 2, parentTabColumn);
	}

	if (card->config.spdif & SPDIF_OUT_PRESENT) {
		ice1712UI_CreateOutput(card, &mmc, 4, parentTabColumn);
	}

	//Internal Mixer Tab
	//Output
	parentTab = ice1712UI_CreateGroup(&mmc, group++, CONTROL_IS_MASTER,
		S_null, string_list[4]);

	for (i = 0; i < card->config.nb_DAC; i += 2) {
		parentTabColumn = ice1712UI_CreateGroup(&mmc, group++,
			parentTab, S_null, string_list[(i / 2) + 11]);
		ice1712UI_CreateChannel(&mmc, channel++, parentTabColumn, NULL);
	}

	if (card->config.spdif & SPDIF_OUT_PRESENT) {
		parentTabColumn = ice1712UI_CreateGroup(&mmc, group++,
			parentTab, S_null, string_list[15]);
		ice1712UI_CreateChannel(&mmc, ICE1712_HARDWARE_VOLUME - 2,
			parentTabColumn, NULL);
	}

	//Input
	channel = ICE1712_HARDWARE_VOLUME;
	for (i = 0; i < card->config.nb_ADC; i += 2) {
		parentTabColumn = ice1712UI_CreateGroup(&mmc, group++,
			parentTab, S_null, string_list[(i / 2) + 17]);
		ice1712UI_CreateChannel(&mmc, channel++, parentTabColumn, NULL);
	}

	if (card->config.spdif & SPDIF_IN_PRESENT) {
		parentTabColumn = ice1712UI_CreateGroup(&mmc, group++,
			parentTab, S_null, string_list[21]);
		ice1712UI_CreateChannel(&mmc, 2 * ICE1712_HARDWARE_VOLUME - 2,
			parentTabColumn, NULL);
	}

	mmci->control_count = nb_control_created;
	ITRACE_VV("Get MixValue Channels: Returned %" B_PRIi32 "\n",
		mmci->control_count);

	return B_OK;
}


/*
 * Not implemented
 */
status_t
ice1712Get_MixValueConnections(ice1712 *card,
	multi_mix_connection_info *data)
{
	data->actual_count = 0;
	return B_OK;
}


status_t
ice1712Buffer_Get(ice1712 *card, multi_buffer_list *data)
{
	const size_t stride_o = MAX_DAC * SAMPLE_SIZE;
	const size_t stride_i = MAX_ADC * SAMPLE_SIZE;
	const uint32 buf_o = stride_o * card->buffer_size;
	const uint32 buf_i = stride_i * card->buffer_size;
	int buff, chan_i = 0, chan_o = 0;

	ITRACE_VV("flags = %#" B_PRIx32 "\n", data->flags);
	ITRACE_VV("request_playback_buffers = %" B_PRIu32 "\n",
			data->request_playback_buffers);
	ITRACE_VV("request_playback_channels = %" B_PRIu32 "\n",
			data->request_playback_channels);
	ITRACE_VV("request_playback_buffer_size = %" B_PRIx32 "\n",
			data->request_playback_buffer_size);
	ITRACE_VV("request_record_buffers = %" B_PRIu32 "\n",
			data->request_record_buffers);
	ITRACE_VV("request_record_channels = %" B_PRIu32 "\n",
			data->request_record_channels);
	ITRACE_VV("request_record_buffer_size = %" B_PRIx32 "\n",
			data->request_record_buffer_size);

	for (buff = 0; buff < SWAPPING_BUFFERS; buff++) {
		if (data->request_playback_channels == card->total_output_channels) {
			for (chan_o = 0; chan_o < card->config.nb_DAC; chan_o++) {
			//Analog STEREO output
				data->playback_buffers[buff][chan_o].base =
					(char*)(card->log_addr_pb + buf_o * buff
						+ SAMPLE_SIZE * chan_o);
				data->playback_buffers[buff][chan_o].stride = stride_o;
				ITRACE_VV("pb_buffer[%d][%d] = %p\n", buff, chan_o,
					data->playback_buffers[buff][chan_o].base);
			}

			if (card->config.spdif & SPDIF_OUT_PRESENT) {
			//SPDIF STEREO output
				data->playback_buffers[buff][chan_o].base =
					(char*)(card->log_addr_pb + buf_o * buff
						+ SAMPLE_SIZE * SPDIF_LEFT);
				data->playback_buffers[buff][chan_o].stride = stride_o;
				ITRACE_VV("pb_buffer[%d][%d] = %p\n", buff, chan_o,
					data->playback_buffers[buff][chan_o].base);

				chan_o++;
				data->playback_buffers[buff][chan_o].base =
					(char*)(card->log_addr_pb + buf_o * buff
						+ SAMPLE_SIZE * SPDIF_RIGHT);
				data->playback_buffers[buff][chan_o].stride = stride_o;
				ITRACE_VV("pb_buffer[%d][%d] = %p\n", buff, chan_o,
					data->playback_buffers[buff][chan_o].base);
				chan_o++;
			}
		}

		if (data->request_record_channels ==
			card->total_input_channels) {
			for (chan_i = 0; chan_i < card->config.nb_ADC; chan_i++) {
			//Analog STEREO input
				data->record_buffers[buff][chan_i].base =
					(char*)(card->log_addr_rec + buf_i * buff
						+ SAMPLE_SIZE * chan_i);
				data->record_buffers[buff][chan_i].stride = stride_i;
				ITRACE_VV("rec_buffer[%d][%d] = %p\n", buff, chan_i,
					data->record_buffers[buff][chan_i].base);
			}

			if (card->config.spdif & SPDIF_IN_PRESENT) {
			//SPDIF STEREO input
				data->record_buffers[buff][chan_i].base =
					(char*)(card->log_addr_rec + buf_i * buff
						+ SAMPLE_SIZE * SPDIF_LEFT);
				data->record_buffers[buff][chan_i].stride = stride_i;
				ITRACE_VV("rec_buffer[%d][%d] = %p\n", buff, chan_i,
					data->record_buffers[buff][chan_i].base);

				chan_i++;
				data->record_buffers[buff][chan_i].base =
					(char*)(card->log_addr_rec + buf_i * buff
						+ SAMPLE_SIZE * SPDIF_RIGHT);
				data->record_buffers[buff][chan_i].stride = stride_i;
				ITRACE_VV("rec_buffer[%d][%d] = %p\n", buff, chan_i,
					data->record_buffers[buff][chan_i].base);
				chan_i++;
			}

			//The digital mixer output
			data->record_buffers[buff][chan_i].base =
				(char*)(card->log_addr_rec + buf_i * buff
					+ SAMPLE_SIZE * MIXER_OUT_LEFT);
			data->record_buffers[buff][chan_i].stride = stride_i;
			ITRACE_VV("rec_buffer[%d][%d] = %p\n", buff, chan_i,
					data->record_buffers[buff][chan_i].base);

			chan_i++;
			data->record_buffers[buff][chan_i].base =
				(char*)(card->log_addr_rec + buf_i * buff
					+ SAMPLE_SIZE * MIXER_OUT_RIGHT);
			data->record_buffers[buff][chan_i].stride = stride_i;
			ITRACE_VV("rec_buffer[%d][%d] = %p\n", buff, chan_i,
					data->record_buffers[buff][chan_i].base);
			chan_i++;
		}
	}

	data->return_playback_buffers = SWAPPING_BUFFERS;
	data->return_playback_channels = card->total_output_channels;
	data->return_playback_buffer_size = card->buffer_size;

	ITRACE("return_playback_buffers = %" B_PRIi32 "\n",
		data->return_playback_buffers);
	ITRACE("return_playback_channels = %" B_PRIi32 "\n",
		data->return_playback_channels);
	ITRACE("return_playback_buffer_size = %" B_PRIu32 "\n",
		data->return_playback_buffer_size);

	data->return_record_buffers = SWAPPING_BUFFERS;
	data->return_record_channels = card->total_input_channels;
	data->return_record_channels = chan_i;
	data->return_record_buffer_size = card->buffer_size;

	ITRACE("return_record_buffers = %" B_PRIi32 "\n",
		data->return_record_buffers);
	ITRACE("return_record_channels = %" B_PRIi32 "\n",
		data->return_record_channels);
	ITRACE("return_record_buffer_size = %" B_PRIu32 "\n",
		data->return_record_buffer_size);

	ice1712Buffer_Start(card);

	return B_OK;
}