xref: /haiku/src/add-ons/kernel/drivers/audio/hda/hda_multi_audio.cpp (revision a706a92d65bae6c531ce69d23ae046378c163b77)
1340dd4feSAxel Dörfler /*
28d797bdaSAxel Dörfler  * Copyright 2007-2010, Haiku, Inc. All rights reserved.
3340dd4feSAxel Dörfler  * Distributed under the terms of the MIT License.
4340dd4feSAxel Dörfler  *
5340dd4feSAxel Dörfler  * Authors:
6340dd4feSAxel Dörfler  *		Ithamar Adema, ithamar AT unet DOT nl
730f55bc9SAxel Dörfler  *		Axel Dörfler, axeld@pinc-software.de
8340dd4feSAxel Dörfler  */
9340dd4feSAxel Dörfler 
10340dd4feSAxel Dörfler 
118aea77d0SHumdinger #include <driver_settings.h>
128aea77d0SHumdinger 
13340dd4feSAxel Dörfler #include "driver.h"
14340dd4feSAxel Dörfler 
154f2fac06SJérôme Duval #include <kernel.h>
164f2fac06SJérôme Duval 
17340dd4feSAxel Dörfler 
18cd803f23SIngo Weinhold //#define TRACE_MULTI_AUDIO
19340dd4feSAxel Dörfler #ifdef TRACE_MULTI_AUDIO
209457b769SAdrien Destugues #	define TRACE(a...) dprintf("hda: " a)
21340dd4feSAxel Dörfler #else
22340dd4feSAxel Dörfler #	define TRACE(a...) ;
23340dd4feSAxel Dörfler #endif
249457b769SAdrien Destugues #define ERROR(a...)	dprintf("hda: " a)
25340dd4feSAxel Dörfler 
26*a706a92dSAugustin Cavalier 
278c3da8ddSJérôme Duval typedef enum {
288c3da8ddSJérôme Duval 	B_MIX_GAIN = 1 << 0,
29ba4d7ed2SJérôme Duval 	B_MIX_MUTE = 1 << 1,
30ba4d7ed2SJérôme Duval 	B_MIX_MUX_MIXER = 1 << 2,
31ba4d7ed2SJérôme Duval 	B_MIX_MUX_SELECTOR = 1 << 3
328c3da8ddSJérôme Duval } mixer_type;
338c3da8ddSJérôme Duval 
34340dd4feSAxel Dörfler 
35340dd4feSAxel Dörfler static multi_channel_info sChannels[] = {
36340dd4feSAxel Dörfler 	{  0, B_MULTI_OUTPUT_CHANNEL,	B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 },
37340dd4feSAxel Dörfler 	{  1, B_MULTI_OUTPUT_CHANNEL,	B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 },
38340dd4feSAxel Dörfler 	{  2, B_MULTI_INPUT_CHANNEL,	B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 },
39340dd4feSAxel Dörfler 	{  3, B_MULTI_INPUT_CHANNEL,	B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 },
40340dd4feSAxel Dörfler 	{  4, B_MULTI_OUTPUT_BUS,		B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS,
41340dd4feSAxel Dörfler 			B_CHANNEL_MINI_JACK_STEREO },
42340dd4feSAxel Dörfler 	{  5, B_MULTI_OUTPUT_BUS,		B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS,
43340dd4feSAxel Dörfler 			B_CHANNEL_MINI_JACK_STEREO },
44340dd4feSAxel Dörfler 	{  6, B_MULTI_INPUT_BUS,		B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS,
45340dd4feSAxel Dörfler 			B_CHANNEL_MINI_JACK_STEREO },
46340dd4feSAxel Dörfler 	{  7, B_MULTI_INPUT_BUS,		B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS,
47340dd4feSAxel Dörfler 			B_CHANNEL_MINI_JACK_STEREO },
48340dd4feSAxel Dörfler };
49340dd4feSAxel Dörfler 
50340dd4feSAxel Dörfler 
51340dd4feSAxel Dörfler static int32
format2size(uint32 format)52340dd4feSAxel Dörfler format2size(uint32 format)
53340dd4feSAxel Dörfler {
54340dd4feSAxel Dörfler 	switch (format) {
55340dd4feSAxel Dörfler 		case B_FMT_8BIT_S:
56340dd4feSAxel Dörfler 		case B_FMT_16BIT:
57340dd4feSAxel Dörfler 			return 2;
58340dd4feSAxel Dörfler 
59340dd4feSAxel Dörfler 		case B_FMT_18BIT:
60c991159aSJérôme Duval 		case B_FMT_20BIT:
61340dd4feSAxel Dörfler 		case B_FMT_24BIT:
62340dd4feSAxel Dörfler 		case B_FMT_32BIT:
63340dd4feSAxel Dörfler 		case B_FMT_FLOAT:
64340dd4feSAxel Dörfler 			return 4;
65340dd4feSAxel Dörfler 
66340dd4feSAxel Dörfler 		default:
67340dd4feSAxel Dörfler 			return -1;
68340dd4feSAxel Dörfler 	}
69340dd4feSAxel Dörfler }
70340dd4feSAxel Dörfler 
71340dd4feSAxel Dörfler 
728aea77d0SHumdinger #define HDA_SETTINGS "hda.settings"
738aea77d0SHumdinger 
748aea77d0SHumdinger static struct {
758aea77d0SHumdinger 	int32 play_buffer_frames;
768aea77d0SHumdinger 	int32 play_buffer_count;
778aea77d0SHumdinger 	int32 record_buffer_frames;
788aea77d0SHumdinger 	int32 record_buffer_count;
798aea77d0SHumdinger } requested_settings;
808aea77d0SHumdinger 
818aea77d0SHumdinger 
828aea77d0SHumdinger void
get_settings_from_file()838aea77d0SHumdinger get_settings_from_file()
848aea77d0SHumdinger {
858aea77d0SHumdinger 	const char *item;
868aea77d0SHumdinger 	char       *end;
878aea77d0SHumdinger 	uint32      value;
888aea77d0SHumdinger 
898aea77d0SHumdinger 	memset(&requested_settings, 0, sizeof(requested_settings));
908aea77d0SHumdinger 	dprintf("looking for settings file\n");
918aea77d0SHumdinger 
928aea77d0SHumdinger 	void *settings_handle = load_driver_settings(HDA_SETTINGS);
938aea77d0SHumdinger 	if (settings_handle == NULL)
948aea77d0SHumdinger 		return;
958aea77d0SHumdinger 
968aea77d0SHumdinger 	item = get_driver_parameter (settings_handle, "play_buffer_frames", NULL,
978aea77d0SHumdinger 		NULL);
988aea77d0SHumdinger 	if (item) {
998aea77d0SHumdinger 		value = strtoul (item, &end, 0);
1008aea77d0SHumdinger 		if (*end == '\0')
1018aea77d0SHumdinger 			requested_settings.play_buffer_frames = value;
1028aea77d0SHumdinger 		}
1038aea77d0SHumdinger 
1048aea77d0SHumdinger 	item = get_driver_parameter (settings_handle, "play_buffer_count", NULL,
1058aea77d0SHumdinger 		NULL);
1068aea77d0SHumdinger 	if (item) {
1078aea77d0SHumdinger 		value = strtoul (item, &end, 0);
1088aea77d0SHumdinger 		if (*end == '\0')
1098aea77d0SHumdinger 			requested_settings.play_buffer_count = value;
1108aea77d0SHumdinger 	}
1118aea77d0SHumdinger 
1128aea77d0SHumdinger 	item = get_driver_parameter (settings_handle, "record_buffer_frames", NULL,
1138aea77d0SHumdinger 		NULL);
1148aea77d0SHumdinger 	if (item) {
1158aea77d0SHumdinger 		value = strtoul (item, &end, 0);
1168aea77d0SHumdinger 		if (*end == '\0')
1178aea77d0SHumdinger 			requested_settings.record_buffer_frames = value;
1188aea77d0SHumdinger 	}
1198aea77d0SHumdinger 
1208aea77d0SHumdinger 	item = get_driver_parameter (settings_handle, "record_buffer_count", NULL,
1218aea77d0SHumdinger 		NULL);
1228aea77d0SHumdinger 	if (item) {
1238aea77d0SHumdinger 		value = strtoul (item, &end, 0);
1248aea77d0SHumdinger 		if (*end == '\0')
1258aea77d0SHumdinger 			requested_settings.record_buffer_count = value;
1268aea77d0SHumdinger 	}
1278aea77d0SHumdinger 
1288aea77d0SHumdinger 	unload_driver_settings(settings_handle);
1298aea77d0SHumdinger }
1308aea77d0SHumdinger 
1318aea77d0SHumdinger 
132340dd4feSAxel Dörfler static status_t
get_description(hda_audio_group * audioGroup,multi_description * data)133340dd4feSAxel Dörfler get_description(hda_audio_group* audioGroup, multi_description* data)
134340dd4feSAxel Dörfler {
135340dd4feSAxel Dörfler 	data->interface_version = B_CURRENT_INTERFACE_VERSION;
136340dd4feSAxel Dörfler 	data->interface_minimum = B_CURRENT_INTERFACE_VERSION;
137340dd4feSAxel Dörfler 
138340dd4feSAxel Dörfler 	strcpy(data->friendly_name, "HD Audio");
139340dd4feSAxel Dörfler 	strcpy(data->vendor_info, "Haiku");
140340dd4feSAxel Dörfler 
14130f55bc9SAxel Dörfler 	int32 inChannels = 0;
14230f55bc9SAxel Dörfler 	if (audioGroup->record_stream != NULL)
14330f55bc9SAxel Dörfler 		inChannels = 2;
14430f55bc9SAxel Dörfler 
14530f55bc9SAxel Dörfler 	int32 outChannels = 0;
14630f55bc9SAxel Dörfler 	if (audioGroup->playback_stream != NULL)
14730f55bc9SAxel Dörfler 		outChannels = 2;
14830f55bc9SAxel Dörfler 
14930f55bc9SAxel Dörfler 	data->output_channel_count = outChannels;
15030f55bc9SAxel Dörfler 	data->output_bus_channel_count = outChannels;
15130f55bc9SAxel Dörfler 	data->input_channel_count = inChannels;
15230f55bc9SAxel Dörfler 	data->input_bus_channel_count = inChannels;
153340dd4feSAxel Dörfler 	data->aux_bus_channel_count = 0;
154340dd4feSAxel Dörfler 
1557848bf79SJérôme Duval 	TRACE("%s: request_channel_count: %" B_PRId32 "\n", __func__,
156340dd4feSAxel Dörfler 		data->request_channel_count);
157340dd4feSAxel Dörfler 
158340dd4feSAxel Dörfler 	if (data->request_channel_count >= (int)(sizeof(sChannels)
159340dd4feSAxel Dörfler 			/ sizeof(sChannels[0]))) {
160340dd4feSAxel Dörfler 		memcpy(data->channels, &sChannels, sizeof(sChannels));
161340dd4feSAxel Dörfler 	}
162340dd4feSAxel Dörfler 
1638d797bdaSAxel Dörfler 	if (audioGroup->playback_stream != NULL) {
164a4df3198SJérôme Duval 		data->output_rates = audioGroup->playback_stream->sample_rate;
1658d797bdaSAxel Dörfler 		data->output_formats = audioGroup->playback_stream->sample_format;
1668d797bdaSAxel Dörfler 	} else {
1678d797bdaSAxel Dörfler 		data->output_rates = 0;
1688d797bdaSAxel Dörfler 		data->output_formats = 0;
1698d797bdaSAxel Dörfler 	}
170340dd4feSAxel Dörfler 
1718d797bdaSAxel Dörfler 	if (audioGroup->record_stream != NULL) {
1728d797bdaSAxel Dörfler 		data->input_rates = audioGroup->record_stream->sample_rate;
1738d797bdaSAxel Dörfler 		data->input_formats = audioGroup->record_stream->sample_format;
1748d797bdaSAxel Dörfler 	} else {
1758d797bdaSAxel Dörfler 		data->input_rates = 0;
1768d797bdaSAxel Dörfler 		data->input_formats = 0;
1778d797bdaSAxel Dörfler 	}
1788d797bdaSAxel Dörfler 
1798d797bdaSAxel Dörfler 	// force existance of 48kHz if variable rates are not supported
180340dd4feSAxel Dörfler 	if (data->output_rates == 0)
181340dd4feSAxel Dörfler 		data->output_rates = B_SR_48000;
182340dd4feSAxel Dörfler 	if (data->input_rates == 0)
183340dd4feSAxel Dörfler 		data->input_rates = B_SR_48000;
184340dd4feSAxel Dörfler 
185340dd4feSAxel Dörfler 	data->max_cvsr_rate = 0;
186340dd4feSAxel Dörfler 	data->min_cvsr_rate = 0;
187340dd4feSAxel Dörfler 
188340dd4feSAxel Dörfler 	data->lock_sources = B_MULTI_LOCK_INTERNAL;
189340dd4feSAxel Dörfler 	data->timecode_sources = 0;
1908d797bdaSAxel Dörfler 	data->interface_flags
1918d797bdaSAxel Dörfler 		= B_MULTI_INTERFACE_PLAYBACK | B_MULTI_INTERFACE_RECORD;
192340dd4feSAxel Dörfler 	data->start_latency = 30000;
193340dd4feSAxel Dörfler 
194340dd4feSAxel Dörfler 	strcpy(data->control_panel, "");
195340dd4feSAxel Dörfler 
196340dd4feSAxel Dörfler 	return B_OK;
197340dd4feSAxel Dörfler }
198340dd4feSAxel Dörfler 
199340dd4feSAxel Dörfler 
200340dd4feSAxel Dörfler static status_t
get_enabled_channels(hda_audio_group * audioGroup,multi_channel_enable * data)201340dd4feSAxel Dörfler get_enabled_channels(hda_audio_group* audioGroup, multi_channel_enable* data)
202340dd4feSAxel Dörfler {
203340dd4feSAxel Dörfler 	data->lock_source = B_MULTI_LOCK_INTERNAL;
204340dd4feSAxel Dörfler 
2054f2fac06SJérôme Duval 	int32 inChannels = 0;
2064f2fac06SJérôme Duval 	if (audioGroup->record_stream != NULL)
2074f2fac06SJérôme Duval 		inChannels = 2;
2084f2fac06SJérôme Duval 
2094f2fac06SJérôme Duval 	int32 outChannels = 0;
2104f2fac06SJérôme Duval 	if (audioGroup->playback_stream != NULL)
2114f2fac06SJérôme Duval 		outChannels = 2;
2124f2fac06SJérôme Duval 
2134f2fac06SJérôme Duval 	uint32 enable_bits = 0;
2144f2fac06SJérôme Duval 	uint32 maxChannels = min_c(32, inChannels + outChannels);
2154f2fac06SJérôme Duval 	for (uint32 i = 0; i < maxChannels; i++)
2164f2fac06SJérôme Duval 		B_SET_CHANNEL(&enable_bits, i, true);
2174f2fac06SJérôme Duval 
218340dd4feSAxel Dörfler 	return B_OK;
219340dd4feSAxel Dörfler }
220340dd4feSAxel Dörfler 
221340dd4feSAxel Dörfler 
222340dd4feSAxel Dörfler static status_t
get_global_format(hda_audio_group * audioGroup,multi_format_info * data)223340dd4feSAxel Dörfler get_global_format(hda_audio_group* audioGroup, multi_format_info* data)
224340dd4feSAxel Dörfler {
225340dd4feSAxel Dörfler 	data->output_latency = 0;
226340dd4feSAxel Dörfler 	data->input_latency = 0;
227340dd4feSAxel Dörfler 	data->timecode_kind = 0;
228340dd4feSAxel Dörfler 
22930f55bc9SAxel Dörfler 	if (audioGroup->playback_stream != NULL) {
230340dd4feSAxel Dörfler 		data->output.format = audioGroup->playback_stream->sample_format;
231340dd4feSAxel Dörfler 		data->output.rate = audioGroup->playback_stream->sample_rate;
23230f55bc9SAxel Dörfler 	} else {
23330f55bc9SAxel Dörfler 		data->output.format = 0;
23430f55bc9SAxel Dörfler 		data->output.rate = 0;
23530f55bc9SAxel Dörfler 	}
236340dd4feSAxel Dörfler 
23730f55bc9SAxel Dörfler 	if (audioGroup->record_stream != NULL) {
238340dd4feSAxel Dörfler 		data->input.format = audioGroup->record_stream->sample_format;
239a4df3198SJérôme Duval 		data->input.rate = audioGroup->record_stream->sample_rate;
24030f55bc9SAxel Dörfler 	} else {
24130f55bc9SAxel Dörfler 		data->input.format = 0;
24230f55bc9SAxel Dörfler 		data->input.rate = 0;
24330f55bc9SAxel Dörfler 	}
244340dd4feSAxel Dörfler 
245340dd4feSAxel Dörfler 	return B_OK;
246340dd4feSAxel Dörfler }
247340dd4feSAxel Dörfler 
248340dd4feSAxel Dörfler 
249340dd4feSAxel Dörfler static status_t
set_global_format(hda_audio_group * audioGroup,multi_format_info * data)250340dd4feSAxel Dörfler set_global_format(hda_audio_group* audioGroup, multi_format_info* data)
251340dd4feSAxel Dörfler {
252340dd4feSAxel Dörfler 	// TODO: it looks like we're not supposed to fail; fix this!
253340dd4feSAxel Dörfler #if 0
254340dd4feSAxel Dörfler 	if ((data->output.format & audioGroup->supported_formats) == 0)
255340dd4feSAxel Dörfler 		|| (data->output.rate & audioGroup->supported_rates) == 0)
256340dd4feSAxel Dörfler 		return B_BAD_VALUE;
257340dd4feSAxel Dörfler #endif
258340dd4feSAxel Dörfler 
25930f55bc9SAxel Dörfler 	if (audioGroup->playback_stream != NULL) {
260340dd4feSAxel Dörfler 		audioGroup->playback_stream->sample_format = data->output.format;
261340dd4feSAxel Dörfler 		audioGroup->playback_stream->sample_rate = data->output.rate;
262340dd4feSAxel Dörfler 		audioGroup->playback_stream->sample_size = format2size(
263340dd4feSAxel Dörfler 			audioGroup->playback_stream->sample_format);
26430f55bc9SAxel Dörfler 	}
265340dd4feSAxel Dörfler 
26630f55bc9SAxel Dörfler 	if (audioGroup->record_stream != NULL) {
267340dd4feSAxel Dörfler 		audioGroup->record_stream->sample_rate = data->input.rate;
268340dd4feSAxel Dörfler 		audioGroup->record_stream->sample_format = data->input.format;
269340dd4feSAxel Dörfler 		audioGroup->record_stream->sample_size = format2size(
270340dd4feSAxel Dörfler 			audioGroup->record_stream->sample_format);
27130f55bc9SAxel Dörfler 	}
272340dd4feSAxel Dörfler 
273340dd4feSAxel Dörfler 	return B_OK;
274340dd4feSAxel Dörfler }
275340dd4feSAxel Dörfler 
276340dd4feSAxel Dörfler 
277ba4d7ed2SJérôme Duval static enum strind_id
hda_find_multi_string(hda_widget & widget)278ba4d7ed2SJérôme Duval hda_find_multi_string(hda_widget& widget)
279ba4d7ed2SJérôme Duval {
280ba4d7ed2SJérôme Duval 	switch (CONF_DEFAULT_DEVICE(widget.d.pin.config)) {
281ba4d7ed2SJérôme Duval 		case PIN_DEV_CD:
282ba4d7ed2SJérôme Duval 			return S_CD;
283ba4d7ed2SJérôme Duval 		case PIN_DEV_LINE_IN:
284ba4d7ed2SJérôme Duval 		case PIN_DEV_LINE_OUT:
285ba4d7ed2SJérôme Duval 			return S_LINE;
286ba4d7ed2SJérôme Duval 		case PIN_DEV_MIC_IN:
287ba4d7ed2SJérôme Duval 			return S_MIC;
288ba4d7ed2SJérôme Duval 		case PIN_DEV_AUX:
289ba4d7ed2SJérôme Duval 			return S_AUX;
290ba4d7ed2SJérôme Duval 		case PIN_DEV_SPDIF_IN:
291ba4d7ed2SJérôme Duval 		case PIN_DEV_SPDIF_OUT:
292ba4d7ed2SJérôme Duval 			return S_SPDIF;
293ba4d7ed2SJérôme Duval 		case PIN_DEV_HEAD_PHONE_OUT:
294ba4d7ed2SJérôme Duval 			return S_HEADPHONE;
295ba4d7ed2SJérôme Duval 	}
2967848bf79SJérôme Duval 	ERROR("couln't find a string for widget %" B_PRIu32 " in "
2977848bf79SJérôme Duval 		"hda_find_multi_string()\n", widget.node_id);
298ba4d7ed2SJérôme Duval 	return S_null;
299ba4d7ed2SJérôme Duval }
300ba4d7ed2SJérôme Duval 
301ba4d7ed2SJérôme Duval 
302e4e70475SJérôme Duval static void
hda_find_multi_custom_string(hda_widget & widget,char * custom,uint32 size)303e4e70475SJérôme Duval hda_find_multi_custom_string(hda_widget& widget, char* custom, uint32 size)
304ba4d7ed2SJérôme Duval {
305e4e70475SJérôme Duval 	const char* device = NULL;
306ba4d7ed2SJérôme Duval 	switch (CONF_DEFAULT_DEVICE(widget.d.pin.config)) {
307ba4d7ed2SJérôme Duval 		case PIN_DEV_LINE_IN:
308ab496a4bSStephan Aßmus 			device = "Line in";
309ba4d7ed2SJérôme Duval 		case PIN_DEV_LINE_OUT:
310e4e70475SJérôme Duval 			if (device == NULL)
311ab496a4bSStephan Aßmus 				device = "Line out";
312ba4d7ed2SJérôme Duval 		case PIN_DEV_MIC_IN:
313e4e70475SJérôme Duval 			if (device == NULL)
314ab496a4bSStephan Aßmus 				device =  "Mic in";
315ba4d7ed2SJérôme Duval 			switch (CONF_DEFAULT_COLOR(widget.d.pin.config)) {
316ba4d7ed2SJérôme Duval 				case 1:
317e4e70475SJérôme Duval 					device = "Rear";
318e4e70475SJérôme Duval 					break;
319ba4d7ed2SJérôme Duval 				case 2:
320e4e70475SJérôme Duval 					device = "Side";
321e4e70475SJérôme Duval 					break;
322ba4d7ed2SJérôme Duval 				case 3:
323ab496a4bSStephan Aßmus 					device = "Line in";
324e4e70475SJérôme Duval 					break;
325ba4d7ed2SJérôme Duval 				case 4:
326e4e70475SJérôme Duval 					device = "Front";
327e4e70475SJérôme Duval 					break;
328ba4d7ed2SJérôme Duval 				case 6:
329e4e70475SJérôme Duval 					device = "Center/Sub";
330e4e70475SJérôme Duval 					break;
331ba4d7ed2SJérôme Duval 				case 9:
332ab496a4bSStephan Aßmus 					device = "Mic in";
333e4e70475SJérôme Duval 					break;
334ba4d7ed2SJérôme Duval 			}
335ba4d7ed2SJérôme Duval 			break;
336ba4d7ed2SJérôme Duval 		case PIN_DEV_SPDIF_IN:
337ab496a4bSStephan Aßmus 			device = "SPDIF in";
338e4e70475SJérôme Duval 			break;
339ba4d7ed2SJérôme Duval 		case PIN_DEV_SPDIF_OUT:
340ab496a4bSStephan Aßmus 			device = "SPDIF out";
341e4e70475SJérôme Duval 			break;
342ba4d7ed2SJérôme Duval 		case PIN_DEV_CD:
343e4e70475SJérôme Duval 			device = "CD";
344e4e70475SJérôme Duval 			break;
345ba4d7ed2SJérôme Duval 		case PIN_DEV_HEAD_PHONE_OUT:
346e4e70475SJérôme Duval 			device = "Headphones";
347e4e70475SJérôme Duval 			break;
348ba4d7ed2SJérôme Duval 		case PIN_DEV_SPEAKER:
349e4e70475SJérôme Duval 			device = "Speaker";
350e4e70475SJérôme Duval 			break;
351ba4d7ed2SJérôme Duval 	}
3528d797bdaSAxel Dörfler 	if (device == NULL) {
3537848bf79SJérôme Duval 		ERROR("couldn't find a string for widget %" B_PRIu32 " in "
354e4e70475SJérôme Duval 			"hda_find_multi_custom_string()\n", widget.node_id);
3558d797bdaSAxel Dörfler 	}
3568d797bdaSAxel Dörfler 
3578d797bdaSAxel Dörfler 	const char* location
3588d797bdaSAxel Dörfler 		= get_widget_location(CONF_DEFAULT_LOCATION(widget.d.pin.config));
359e4e70475SJérôme Duval 	snprintf(custom, size, "%s%s%s", location ? location : "",
360e4e70475SJérôme Duval 		location ? " " : "", device);
361ba4d7ed2SJérôme Duval }
362ba4d7ed2SJérôme Duval 
363ba4d7ed2SJérôme Duval 
3648c3da8ddSJérôme Duval static int32
hda_create_group_control(hda_multi * multi,uint32 * index,int32 parent,enum strind_id string,const char * name)3658c3da8ddSJérôme Duval hda_create_group_control(hda_multi *multi, uint32 *index, int32 parent,
366e4e70475SJérôme Duval 	enum strind_id string, const char* name)
367e4e70475SJérôme Duval {
3688c3da8ddSJérôme Duval 	uint32 i = *index;
3698c3da8ddSJérôme Duval 	(*index)++;
3708c3da8ddSJérôme Duval 	multi->controls[i].mix_control.id = MULTI_CONTROL_FIRSTID + i;
3718c3da8ddSJérôme Duval 	multi->controls[i].mix_control.parent = parent;
3728c3da8ddSJérôme Duval 	multi->controls[i].mix_control.flags = B_MULTI_MIX_GROUP;
3738c3da8ddSJérôme Duval 	multi->controls[i].mix_control.master = MULTI_CONTROL_MASTERID;
3748c3da8ddSJérôme Duval 	multi->controls[i].mix_control.string = string;
3758c3da8ddSJérôme Duval 	if (name)
3768c3da8ddSJérôme Duval 		strcpy(multi->controls[i].mix_control.name, name);
3778c3da8ddSJérôme Duval 
3788c3da8ddSJérôme Duval 	return multi->controls[i].mix_control.id;
3798c3da8ddSJérôme Duval }
3808c3da8ddSJérôme Duval 
3818c3da8ddSJérôme Duval 
3828c3da8ddSJérôme Duval static void
hda_create_channel_control(hda_multi * multi,uint32 * index,int32 parent,int32 string,hda_widget & widget,bool input,uint32 capabilities,int32 inputIndex,bool & gain,bool & mute)383e4e70475SJérôme Duval hda_create_channel_control(hda_multi* multi, uint32* index, int32 parent,
384e4e70475SJérôme Duval 	int32 string, hda_widget& widget, bool input, uint32 capabilities,
385e4e70475SJérôme Duval 	int32 inputIndex, bool& gain, bool& mute)
386e4e70475SJérôme Duval {
3878c3da8ddSJérôme Duval 	uint32 i = *index, id;
3888c3da8ddSJérôme Duval 	hda_multi_mixer_control control;
3898c3da8ddSJérôme Duval 
3908c3da8ddSJérôme Duval 	control.nid = widget.node_id;
3918c3da8ddSJérôme Duval 	control.input = input;
3928c3da8ddSJérôme Duval 	control.mute = 0;
3938c3da8ddSJérôme Duval 	control.gain = 0;
3948c3da8ddSJérôme Duval 	control.capabilities = capabilities;
3958c3da8ddSJérôme Duval 	control.index = inputIndex;
3968c3da8ddSJérôme Duval 	control.mix_control.master = MULTI_CONTROL_MASTERID;
3978c3da8ddSJérôme Duval 	control.mix_control.parent = parent;
3988c3da8ddSJérôme Duval 
3998d797bdaSAxel Dörfler 	if (mute && (capabilities & AMP_CAP_MUTE) != 0) {
40016578b34SJérôme Duval 		control.mix_control.id = MULTI_CONTROL_FIRSTID + i;
40116578b34SJérôme Duval 		control.mix_control.flags = B_MULTI_MIX_ENABLE;
40216578b34SJérôme Duval 		control.mix_control.string = S_MUTE;
40316578b34SJérôme Duval 		control.type = B_MIX_MUTE;
40416578b34SJérôme Duval 		multi->controls[i++] = control;
4057848bf79SJérôme Duval 		TRACE("control nid %" B_PRIu32 " mute\n", control.nid);
40616578b34SJérôme Duval 		mute = false;
40716578b34SJérôme Duval 	}
40816578b34SJérôme Duval 
40916578b34SJérôme Duval 	if (gain && AMP_CAP_NUM_STEPS(capabilities) >= 1) {
4108c3da8ddSJérôme Duval 		control.mix_control.gain.granularity = AMP_CAP_STEP_SIZE(capabilities);
4118c3da8ddSJérôme Duval 		control.mix_control.gain.min_gain = (0.0 - AMP_CAP_OFFSET(capabilities))
4128c3da8ddSJérôme Duval 			* control.mix_control.gain.granularity;
413e4e70475SJérôme Duval 		control.mix_control.gain.max_gain = (AMP_CAP_NUM_STEPS(capabilities)
4148d797bdaSAxel Dörfler 				- AMP_CAP_OFFSET(capabilities))
4158d797bdaSAxel Dörfler 			* control.mix_control.gain.granularity;
4168c3da8ddSJérôme Duval 
4178c3da8ddSJérôme Duval 		control.mix_control.id = MULTI_CONTROL_FIRSTID + i;
4188c3da8ddSJérôme Duval 		control.mix_control.flags = B_MULTI_MIX_GAIN;
4198c3da8ddSJérôme Duval 		control.mix_control.string = S_null;
4208c3da8ddSJérôme Duval 		control.type = B_MIX_GAIN;
4218c3da8ddSJérôme Duval 		strcpy(control.mix_control.name, "Gain");
4228c3da8ddSJérôme Duval 		multi->controls[i++] = control;
4238c3da8ddSJérôme Duval 		id = control.mix_control.id;
4248c3da8ddSJérôme Duval 
4258c3da8ddSJérôme Duval 		// second channel
4268c3da8ddSJérôme Duval 		control.mix_control.id = MULTI_CONTROL_FIRSTID + i;
4278c3da8ddSJérôme Duval 		control.mix_control.master = id;
4288c3da8ddSJérôme Duval 		multi->controls[i++] = control;
4297848bf79SJérôme Duval 		TRACE("control nid %" B_PRIu32 " %f min %f max %f\n", control.nid,
430e4e70475SJérôme Duval 			control.mix_control.gain.granularity,
4318d797bdaSAxel Dörfler 			control.mix_control.gain.min_gain,
4328d797bdaSAxel Dörfler 			control.mix_control.gain.max_gain);
43316578b34SJérôme Duval 		gain = false;
4348c3da8ddSJérôme Duval 	}
4358c3da8ddSJérôme Duval 
4368c3da8ddSJérôme Duval 	*index = i;
4378c3da8ddSJérôme Duval }
4388c3da8ddSJérôme Duval 
4398c3da8ddSJérôme Duval 
440ba4d7ed2SJérôme Duval static void
hda_create_mux_control(hda_multi * multi,uint32 * index,int32 parent,hda_widget & widget)441e4e70475SJérôme Duval hda_create_mux_control(hda_multi *multi, uint32 *index, int32 parent,
442e4e70475SJérôme Duval 	hda_widget& widget)
443e4e70475SJérôme Duval {
444ba4d7ed2SJérôme Duval 	uint32 i = *index, parent2;
445ba4d7ed2SJérôme Duval 	hda_multi_mixer_control control;
446ba4d7ed2SJérôme Duval 	hda_audio_group *audioGroup = multi->group;
447ba4d7ed2SJérôme Duval 
448ba4d7ed2SJérôme Duval 	control.nid = widget.node_id;
449ba4d7ed2SJérôme Duval 	control.input = true;
450ba4d7ed2SJérôme Duval 	control.mute = 0;
451ba4d7ed2SJérôme Duval 	control.gain = 0;
452ba4d7ed2SJérôme Duval 	control.mix_control.master = MULTI_CONTROL_MASTERID;
453ba4d7ed2SJérôme Duval 	control.mix_control.parent = parent;
454ba4d7ed2SJérôme Duval 	control.mix_control.id = MULTI_CONTROL_FIRSTID + i;
455ba4d7ed2SJérôme Duval 	control.mix_control.flags = B_MULTI_MIX_MUX;
456ba4d7ed2SJérôme Duval 	control.mix_control.string = S_null;
4578d797bdaSAxel Dörfler 	control.type = widget.type == WT_AUDIO_MIXER
4588d797bdaSAxel Dörfler 		? B_MIX_MUX_MIXER : B_MIX_MUX_SELECTOR;
459ba4d7ed2SJérôme Duval 	multi->controls[i] = control;
460ba4d7ed2SJérôme Duval 	strcpy(multi->controls[i].mix_control.name, "");
461ba4d7ed2SJérôme Duval 	i++;
462ba4d7ed2SJérôme Duval 	parent2 = control.mix_control.id;
463ba4d7ed2SJérôme Duval 
464ba4d7ed2SJérôme Duval 	for (uint32 j = 0; j < widget.num_inputs; j++) {
465e4e70475SJérôme Duval 		hda_widget *input =
466e4e70475SJérôme Duval 			hda_audio_group_get_widget(audioGroup, widget.inputs[j]);
467ba4d7ed2SJérôme Duval 		if (input->type != WT_PIN_COMPLEX)
468ba4d7ed2SJérôme Duval 			continue;
469ba4d7ed2SJérôme Duval 		control.nid = widget.node_id;
470ba4d7ed2SJérôme Duval 		control.input = true;
471ba4d7ed2SJérôme Duval 		control.mix_control.id = MULTI_CONTROL_FIRSTID + i;
472ba4d7ed2SJérôme Duval 		control.mix_control.flags = B_MULTI_MIX_MUX_VALUE;
473ba4d7ed2SJérôme Duval 		control.mix_control.parent = parent2;
474ba4d7ed2SJérôme Duval 		control.mix_control.string = S_null;
475ba4d7ed2SJérôme Duval 		multi->controls[i] = control;
4768d797bdaSAxel Dörfler 		hda_find_multi_custom_string(*input,
4778d797bdaSAxel Dörfler 			multi->controls[i].mix_control.name,
478e4e70475SJérôme Duval 			sizeof(multi->controls[i].mix_control.name));
479ba4d7ed2SJérôme Duval 		i++;
4808c3da8ddSJérôme Duval 	}
4818c3da8ddSJérôme Duval 
482ba4d7ed2SJérôme Duval 	*index = i;
4838c3da8ddSJérôme Duval }
4848c3da8ddSJérôme Duval 
4858c3da8ddSJérôme Duval 
48616578b34SJérôme Duval static void
hda_create_control_for_complex(hda_multi * multi,uint32 * index,uint32 parent,hda_widget & widget,bool & gain,bool & mute)48716578b34SJérôme Duval hda_create_control_for_complex(hda_multi* multi, uint32* index, uint32 parent,
48816578b34SJérôme Duval 	hda_widget& widget, bool& gain, bool& mute)
48916578b34SJérôme Duval {
49016578b34SJérôme Duval 	hda_audio_group* audioGroup = multi->group;
49116578b34SJérôme Duval 
49216578b34SJérôme Duval 	switch (widget.type) {
49316578b34SJérôme Duval 		case WT_AUDIO_OUTPUT:
49416578b34SJérôme Duval 		case WT_AUDIO_MIXER:
49516578b34SJérôme Duval 		case WT_AUDIO_SELECTOR:
49616578b34SJérôme Duval 		case WT_PIN_COMPLEX:
49716578b34SJérôme Duval 			break;
49816578b34SJérôme Duval 		default:
49916578b34SJérôme Duval 			return;
50016578b34SJérôme Duval 	}
50116578b34SJérôme Duval 
5028d797bdaSAxel Dörfler 	if ((widget.flags & WIDGET_FLAG_WIDGET_PATH) != 0)
50316578b34SJérôme Duval 		return;
50416578b34SJérôme Duval 
5057848bf79SJérôme Duval 	TRACE("  create widget nid %" B_PRIu32 "\n", widget.node_id);
50616578b34SJérôme Duval 	hda_create_channel_control(multi, index, parent, 0,
50716578b34SJérôme Duval 		widget, false, widget.capabilities.output_amplifier, 0, gain, mute);
50816578b34SJérôme Duval 
50916578b34SJérôme Duval 	if (!gain && !mute) {
51016578b34SJérôme Duval 		widget.flags |= WIDGET_FLAG_WIDGET_PATH;
51116578b34SJérôme Duval 		return;
51216578b34SJérôme Duval 	}
51316578b34SJérôme Duval 
514b7bb5837SJérôme Duval 	if (widget.type == WT_AUDIO_MIXER) {
51516578b34SJérôme Duval 		hda_create_channel_control(multi, index, parent, 0,
51616578b34SJérôme Duval 			widget, true, widget.capabilities.input_amplifier, 0, gain, mute);
51716578b34SJérôme Duval 		if (!gain && !mute) {
51816578b34SJérôme Duval 			widget.flags |= WIDGET_FLAG_WIDGET_PATH;
51916578b34SJérôme Duval 			return;
52016578b34SJérôme Duval 		}
52116578b34SJérôme Duval 	}
52216578b34SJérôme Duval 
523b7bb5837SJérôme Duval 	if (widget.type != WT_AUDIO_OUTPUT && widget.num_inputs > 0) {
524e4e70475SJérôme Duval 		hda_widget& child = *hda_audio_group_get_widget(audioGroup,
525e4e70475SJérôme Duval 			widget.inputs[widget.active_input]);
52616578b34SJérôme Duval 		hda_create_control_for_complex(multi, index, parent, child, gain, mute);
52716578b34SJérôme Duval 	}
52816578b34SJérôme Duval 
52916578b34SJérôme Duval 	widget.flags |= WIDGET_FLAG_WIDGET_PATH;
53016578b34SJérôme Duval }
53116578b34SJérôme Duval 
53216578b34SJérôme Duval 
5338c3da8ddSJérôme Duval static status_t
hda_create_controls_list(hda_multi * multi)5348c3da8ddSJérôme Duval hda_create_controls_list(hda_multi* multi)
5358c3da8ddSJérôme Duval {
5368d797bdaSAxel Dörfler 	uint32 index = 0;
5378c3da8ddSJérôme Duval 	hda_audio_group* audioGroup = multi->group;
5388c3da8ddSJérôme Duval 
5398d797bdaSAxel Dörfler 	uint32 parent = hda_create_group_control(multi, &index, 0, S_OUTPUT, NULL);
5408d797bdaSAxel Dörfler 	uint32 parent2;
5418c3da8ddSJérôme Duval 
5428c3da8ddSJérôme Duval 	for (uint32 i = 0; i < audioGroup->widget_count; i++) {
5438c3da8ddSJérôme Duval 		hda_widget& complex = audioGroup->widgets[i];
544e4e70475SJérôme Duval 		char name[48];
5458c3da8ddSJérôme Duval 
5468c3da8ddSJérôme Duval 		if (complex.type != WT_PIN_COMPLEX)
5478c3da8ddSJérôme Duval 			continue;
548c71a6614SJérôme Duval 		if (!PIN_CAP_IS_OUTPUT(complex.d.pin.capabilities))
5498c3da8ddSJérôme Duval 			continue;
55016578b34SJérôme Duval 		if ((complex.flags & WIDGET_FLAG_OUTPUT_PATH) == 0)
55116578b34SJérôme Duval 			continue;
5528c3da8ddSJérôme Duval 
5537848bf79SJérôme Duval 		TRACE("create complex nid %" B_PRIu32 "\n", complex.node_id);
554e4e70475SJérôme Duval 		hda_find_multi_custom_string(complex, name, sizeof(name));
5558d797bdaSAxel Dörfler 		parent2 = hda_create_group_control(multi, &index, parent, S_null, name);
55616578b34SJérôme Duval 		bool gain = true, mute = true;
5578c3da8ddSJérôme Duval 
5588d797bdaSAxel Dörfler 		hda_create_control_for_complex(multi, &index, parent2, complex, gain,
5598d797bdaSAxel Dörfler 			mute);
5608c3da8ddSJérôme Duval 	}
5618c3da8ddSJérôme Duval 
5628c3da8ddSJérôme Duval 	for (uint32 i = 0; i < audioGroup->widget_count; i++) {
5638c3da8ddSJérôme Duval 		hda_widget& widget = audioGroup->widgets[i];
5648c3da8ddSJérôme Duval 
5658c3da8ddSJérôme Duval 		if (widget.type != WT_AUDIO_MIXER)
5668c3da8ddSJérôme Duval 			continue;
5678d797bdaSAxel Dörfler 		if ((widget.flags & WIDGET_FLAG_WIDGET_PATH) != 0)
5688c3da8ddSJérôme Duval 			continue;
5698c3da8ddSJérôme Duval 
5707848bf79SJérôme Duval 		TRACE("create widget nid %" B_PRIu32 "\n", widget.node_id);
5718c3da8ddSJérôme Duval 
5728c3da8ddSJérôme Duval 		if (AMP_CAP_NUM_STEPS(widget.capabilities.input_amplifier) >= 1) {
5738c3da8ddSJérôme Duval 			for (uint32 j = 0; j < widget.num_inputs; j++) {
574e4e70475SJérôme Duval 				hda_widget* complex = hda_audio_group_get_widget(audioGroup,
575e4e70475SJérôme Duval 					widget.inputs[j]);
576e4e70475SJérôme Duval 				char name[48];
5778c3da8ddSJérôme Duval 				if (complex->type != WT_PIN_COMPLEX)
5788c3da8ddSJérôme Duval 					continue;
579c71a6614SJérôme Duval 				if (!PIN_CAP_IS_INPUT(complex->d.pin.capabilities))
580b990fd92SJérôme Duval 					continue;
5818d797bdaSAxel Dörfler 				if ((complex->flags & WIDGET_FLAG_OUTPUT_PATH) != 0)
5828c3da8ddSJérôme Duval 					continue;
5837848bf79SJérôme Duval 				TRACE("  create widget input nid %" B_PRIu32 "\n",
5847848bf79SJérôme Duval 					widget.inputs[j]);
585e4e70475SJérôme Duval 				hda_find_multi_custom_string(*complex, name, sizeof(name));
5868c3da8ddSJérôme Duval 				parent2 = hda_create_group_control(multi, &index,
587e4e70475SJérôme Duval 					parent, S_null, name);
58816578b34SJérôme Duval 				bool gain = true, mute = true;
589e4e70475SJérôme Duval 				hda_create_channel_control(multi, &index, parent2, 0, widget,
590e4e70475SJérôme Duval 					true, widget.capabilities.input_amplifier, j, gain, mute);
5918c3da8ddSJérôme Duval 			}
5928c3da8ddSJérôme Duval 		}
5938c3da8ddSJérôme Duval 
5948c3da8ddSJérôme Duval 		widget.flags |= WIDGET_FLAG_WIDGET_PATH;
5958c3da8ddSJérôme Duval 	}
5968c3da8ddSJérôme Duval 
5978c3da8ddSJérôme Duval 	parent = hda_create_group_control(multi, &index, 0, S_INPUT, NULL);
5988c3da8ddSJérôme Duval 
5998c3da8ddSJérôme Duval 	for (uint32 i = 0; i < audioGroup->widget_count; i++) {
6008c3da8ddSJérôme Duval 		hda_widget& widget = audioGroup->widgets[i];
6018c3da8ddSJérôme Duval 
6028c3da8ddSJérôme Duval 		if (widget.type != WT_AUDIO_INPUT)
6038c3da8ddSJérôme Duval 			continue;
6048c3da8ddSJérôme Duval 
6058c3da8ddSJérôme Duval 		uint32 capabilities = widget.capabilities.input_amplifier;
6068c3da8ddSJérôme Duval 		if (AMP_CAP_NUM_STEPS(capabilities) < 1)
6078c3da8ddSJérôme Duval 			continue;
6088c3da8ddSJérôme Duval 
6098c3da8ddSJérôme Duval 		parent2 = hda_create_group_control(multi, &index,
6108c3da8ddSJérôme Duval 			parent, hda_find_multi_string(widget), "Input");
61116578b34SJérôme Duval 		bool gain = true, mute = true;
6128c3da8ddSJérôme Duval 		hda_create_channel_control(multi, &index, parent2, 0,
61316578b34SJérôme Duval 			widget, true, capabilities, 0, gain, mute);
614ba4d7ed2SJérôme Duval 
615b7bb5837SJérôme Duval 		if (widget.num_inputs > 1) {
6167848bf79SJérôme Duval 			TRACE("  create mux for nid %" B_PRIu32 "\n", widget.node_id);
617b7bb5837SJérôme Duval 			hda_create_mux_control(multi, &index, parent2, widget);
618b7bb5837SJérôme Duval 			continue;
619b7bb5837SJérôme Duval 		}
620b7bb5837SJérôme Duval 
621e4e70475SJérôme Duval 		hda_widget *mixer = hda_audio_group_get_widget(audioGroup,
622e4e70475SJérôme Duval 			widget.inputs[0]);
623ba4d7ed2SJérôme Duval 		if (mixer->type != WT_AUDIO_MIXER && mixer->type != WT_AUDIO_SELECTOR)
624ba4d7ed2SJérôme Duval 			continue;
6257848bf79SJérôme Duval 		TRACE("  create mixer nid %" B_PRIu32 "\n", mixer->node_id);
626ba4d7ed2SJérôme Duval 		hda_create_mux_control(multi, &index, parent2, *mixer);
6278c3da8ddSJérôme Duval 	}
6288c3da8ddSJérôme Duval 
6298c3da8ddSJérôme Duval 	multi->control_count = index;
6307848bf79SJérôme Duval 	TRACE("multi->control_count %" B_PRIu32 "\n", multi->control_count);
6318c3da8ddSJérôme Duval 	return B_OK;
6328c3da8ddSJérôme Duval }
6338c3da8ddSJérôme Duval 
6348c3da8ddSJérôme Duval 
6358c3da8ddSJérôme Duval static status_t
list_mix_controls(hda_audio_group * audioGroup,multi_mix_control_info * mmci)6365076aaf0SJérôme Duval list_mix_controls(hda_audio_group* audioGroup, multi_mix_control_info* mmci)
6378c3da8ddSJérôme Duval {
6385076aaf0SJérôme Duval 	multi_mix_control *mmc = mmci->controls;
6395076aaf0SJérôme Duval 	if (mmci->control_count < 24)
6408c3da8ddSJérôme Duval 		return B_ERROR;
6418c3da8ddSJérôme Duval 
6428c3da8ddSJérôme Duval 	if (hda_create_controls_list(audioGroup->multi) < B_OK)
6438c3da8ddSJérôme Duval 		return B_ERROR;
64479d4b99eSJérôme Duval 	for (uint32 i = 0; i < audioGroup->multi->control_count; i++) {
6455076aaf0SJérôme Duval 		mmc[i] = audioGroup->multi->controls[i].mix_control;
6468c3da8ddSJérôme Duval 	}
6478c3da8ddSJérôme Duval 
6485076aaf0SJérôme Duval 	mmci->control_count = audioGroup->multi->control_count;
649340dd4feSAxel Dörfler 	return B_OK;
650340dd4feSAxel Dörfler }
651340dd4feSAxel Dörfler 
652340dd4feSAxel Dörfler 
653340dd4feSAxel Dörfler static status_t
list_mix_connections(hda_audio_group * audioGroup,multi_mix_connection_info * data)654340dd4feSAxel Dörfler list_mix_connections(hda_audio_group* audioGroup,
655340dd4feSAxel Dörfler 	multi_mix_connection_info* data)
656340dd4feSAxel Dörfler {
657340dd4feSAxel Dörfler 	data->actual_count = 0;
658340dd4feSAxel Dörfler 	return B_OK;
659340dd4feSAxel Dörfler }
660340dd4feSAxel Dörfler 
661340dd4feSAxel Dörfler 
662340dd4feSAxel Dörfler static status_t
list_mix_channels(hda_audio_group * audioGroup,multi_mix_channel_info * data)663340dd4feSAxel Dörfler list_mix_channels(hda_audio_group* audioGroup, multi_mix_channel_info *data)
664340dd4feSAxel Dörfler {
665340dd4feSAxel Dörfler 	return B_OK;
666340dd4feSAxel Dörfler }
667340dd4feSAxel Dörfler 
668340dd4feSAxel Dörfler 
6698c3da8ddSJérôme Duval static void
get_control_gain_mute(hda_audio_group * audioGroup,hda_multi_mixer_control * control,uint32 * resp)670e4e70475SJérôme Duval get_control_gain_mute(hda_audio_group* audioGroup,
671e4e70475SJérôme Duval 	hda_multi_mixer_control *control, uint32 *resp)
6728c3da8ddSJérôme Duval {
6738c3da8ddSJérôme Duval 	uint32 verb[2];
6748c3da8ddSJérôme Duval 	verb[0] = MAKE_VERB(audioGroup->codec->addr,
6758c3da8ddSJérôme Duval 		control->nid,
6768c3da8ddSJérôme Duval 		VID_GET_AMPLIFIER_GAIN_MUTE,
6778c3da8ddSJérôme Duval 		(control->input ? AMP_GET_INPUT : AMP_GET_OUTPUT)
6788c3da8ddSJérôme Duval 		| AMP_GET_LEFT_CHANNEL | AMP_GET_INPUT_INDEX(control->index));
6798c3da8ddSJérôme Duval 	verb[1] = MAKE_VERB(audioGroup->codec->addr,
6808c3da8ddSJérôme Duval 		control->nid,
6818c3da8ddSJérôme Duval 		VID_GET_AMPLIFIER_GAIN_MUTE,
6828c3da8ddSJérôme Duval 		(control->input ? AMP_GET_INPUT : AMP_GET_OUTPUT)
6838c3da8ddSJérôme Duval 		| AMP_GET_RIGHT_CHANNEL | AMP_GET_INPUT_INDEX(control->index));
6848c3da8ddSJérôme Duval 	hda_send_verbs(audioGroup->codec, verb, resp, 2);
6858c3da8ddSJérôme Duval }
6868c3da8ddSJérôme Duval 
6878c3da8ddSJérôme Duval 
6888c3da8ddSJérôme Duval static status_t
get_mix(hda_audio_group * audioGroup,multi_mix_value_info * mmvi)6895076aaf0SJérôme Duval get_mix(hda_audio_group* audioGroup, multi_mix_value_info * mmvi)
6908c3da8ddSJérôme Duval {
6919e806185SAdrien Destugues 	int32 id;
6928c3da8ddSJérôme Duval 	hda_multi_mixer_control *control = NULL;
6935076aaf0SJérôme Duval 	for (int32 i = 0; i < mmvi->item_count; i++) {
6945076aaf0SJérôme Duval 		id = mmvi->values[i].id - MULTI_CONTROL_FIRSTID;
6957848bf79SJérôme Duval 		if (id < 0 || id >= (int32)audioGroup->multi->control_count) {
6967848bf79SJérôme Duval 			dprintf("hda: get_mix : invalid control id requested : %" B_PRId32
6977848bf79SJérôme Duval 				"\n", id);
6988c3da8ddSJérôme Duval 			continue;
6998c3da8ddSJérôme Duval 		}
7008c3da8ddSJérôme Duval 		control = &audioGroup->multi->controls[id];
7018c3da8ddSJérôme Duval 
7028d797bdaSAxel Dörfler 		if ((control->mix_control.flags
7038d797bdaSAxel Dörfler 				& (B_MULTI_MIX_GAIN | B_MULTI_MIX_ENABLE)) != 0) {
7048c3da8ddSJérôme Duval 			uint32 resp[2];
7058c3da8ddSJérôme Duval 			get_control_gain_mute(audioGroup, control, resp);
7068d797bdaSAxel Dörfler 			if ((control->mix_control.flags & B_MULTI_MIX_ENABLE) != 0) {
7075076aaf0SJérôme Duval 				mmvi->values[i].enable = (resp[0] & AMP_MUTE) != 0;
7087848bf79SJérôme Duval 				TRACE("get_mix: %" B_PRId32 " mute: %d\n", control->nid,
7098d797bdaSAxel Dörfler 					mmvi->values[i].enable);
7108d797bdaSAxel Dörfler 			} else if ((control->mix_control.flags & B_MULTI_MIX_GAIN) != 0) {
7118c3da8ddSJérôme Duval 				uint32 value;
7128c3da8ddSJérôme Duval 				if (control->mix_control.master == MULTI_CONTROL_MASTERID)
7138c3da8ddSJérôme Duval 					value = resp[0] & AMP_GAIN_MASK;
7148c3da8ddSJérôme Duval 				else
7158c3da8ddSJérôme Duval 					value = resp[1] & AMP_GAIN_MASK;
7165076aaf0SJérôme Duval 				mmvi->values[i].gain = (0.0 + value - AMP_CAP_OFFSET(control->capabilities))
7178c3da8ddSJérôme Duval 						* AMP_CAP_STEP_SIZE(control->capabilities);
7187848bf79SJérôme Duval 				TRACE("get_mix: %" B_PRId32 " gain: %f (%" B_PRIu32 ")\n",
7197848bf79SJérôme Duval 					control->nid, mmvi->values[i].gain, value);
7208c3da8ddSJérôme Duval 			}
7218c3da8ddSJérôme Duval 
7228d797bdaSAxel Dörfler 		} else if ((control->mix_control.flags & B_MIX_MUX_MIXER) != 0) {
7238d797bdaSAxel Dörfler 			hda_widget* mixer = hda_audio_group_get_widget(audioGroup,
7248d797bdaSAxel Dörfler 				control->nid);
725ba4d7ed2SJérôme Duval 			mmvi->values[i].mux = 0;
726ba4d7ed2SJérôme Duval 			for (uint32 j = 0; j < mixer->num_inputs; j++) {
727ba4d7ed2SJérôme Duval 				uint32 verb = MAKE_VERB(audioGroup->codec->addr,
728ba4d7ed2SJérôme Duval 					control->nid, VID_GET_AMPLIFIER_GAIN_MUTE, AMP_GET_INPUT
729ba4d7ed2SJérôme Duval 					| AMP_GET_LEFT_CHANNEL | AMP_GET_INPUT_INDEX(j));
730ba4d7ed2SJérôme Duval 				uint32 resp;
731ba4d7ed2SJérôme Duval 				if (hda_send_verbs(audioGroup->codec, &verb, &resp, 1) == B_OK) {
7327848bf79SJérôme Duval 					TRACE("get_mix: %" B_PRId32 " mixer %" B_PRIu32
7337848bf79SJérôme Duval 						" is %smute\n", control->nid,
7348d797bdaSAxel Dörfler 						j, (resp & AMP_MUTE) != 0 ? "" : "un");
735ba4d7ed2SJérôme Duval 					if ((resp & AMP_MUTE) == 0) {
736ba4d7ed2SJérôme Duval 						mmvi->values[i].mux = j;
737ba4d7ed2SJérôme Duval #ifndef TRACE_MULTI_AUDIO
738ba4d7ed2SJérôme Duval 						break;
739ba4d7ed2SJérôme Duval #endif
7408c3da8ddSJérôme Duval 					}
741ba4d7ed2SJérôme Duval 				}
742ba4d7ed2SJérôme Duval 			}
7437848bf79SJérôme Duval 			TRACE("get_mix: %" B_PRId32 " mixer: %" B_PRIu32 "\n",
7447848bf79SJérôme Duval 				control->nid, mmvi->values[i].mux);
7458d797bdaSAxel Dörfler 		} else if ((control->mix_control.flags & B_MIX_MUX_SELECTOR) != 0) {
746ba4d7ed2SJérôme Duval 			uint32 verb = MAKE_VERB(audioGroup->codec->addr, control->nid,
747ba4d7ed2SJérôme Duval 				VID_GET_CONNECTION_SELECT, 0);
748ba4d7ed2SJérôme Duval 			uint32 resp;
749ba4d7ed2SJérôme Duval 			if (hda_send_verbs(audioGroup->codec, &verb, &resp, 1) == B_OK)
750ba4d7ed2SJérôme Duval 				mmvi->values[i].mux = resp & 0xff;
7517848bf79SJérôme Duval 			TRACE("get_mix: %" B_PRId32 " selector: %" B_PRIu32 "\n",
7527848bf79SJérôme Duval 				control->nid, mmvi->values[i].mux);
753ba4d7ed2SJérôme Duval 		}
7548c3da8ddSJérôme Duval 	}
7558c3da8ddSJérôme Duval 	return B_OK;
7568c3da8ddSJérôme Duval }
7578c3da8ddSJérôme Duval 
7588c3da8ddSJérôme Duval 
7598c3da8ddSJérôme Duval static status_t
set_mix(hda_audio_group * audioGroup,multi_mix_value_info * mmvi)7605076aaf0SJérôme Duval set_mix(hda_audio_group* audioGroup, multi_mix_value_info * mmvi)
7618c3da8ddSJérôme Duval {
7629e806185SAdrien Destugues 	int32 id;
7638c3da8ddSJérôme Duval 	hda_multi_mixer_control *control = NULL;
7645076aaf0SJérôme Duval 	for (int32 i = 0; i < mmvi->item_count; i++) {
7655076aaf0SJérôme Duval 		id = mmvi->values[i].id - MULTI_CONTROL_FIRSTID;
7667848bf79SJérôme Duval 		if (id < 0 || id >= (int32)audioGroup->multi->control_count) {
7677848bf79SJérôme Duval 			dprintf("set_mix : invalid control id requested : %" B_PRId32 "\n",
7687848bf79SJérôme Duval 				id);
7698c3da8ddSJérôme Duval 			continue;
7708c3da8ddSJérôme Duval 		}
7718c3da8ddSJérôme Duval 		control = &audioGroup->multi->controls[id];
7728c3da8ddSJérôme Duval 
7738d797bdaSAxel Dörfler 		if ((control->mix_control.flags & B_MULTI_MIX_ENABLE) != 0) {
7745076aaf0SJérôme Duval 			control->mute = (mmvi->values[i].enable ? AMP_MUTE : 0);
7757848bf79SJérôme Duval 			TRACE("set_mix: %" B_PRId32 " mute: %" B_PRIx32 "\n", control->nid,
7767848bf79SJérôme Duval 				control->mute);
7778c3da8ddSJérôme Duval 			uint32 resp[2];
7788c3da8ddSJérôme Duval 			get_control_gain_mute(audioGroup, control, resp);
7798c3da8ddSJérôme Duval 
7808c3da8ddSJérôme Duval 			uint32 verb[2];
7818c3da8ddSJérôme Duval 			verb[0] = MAKE_VERB(audioGroup->codec->addr,
7828c3da8ddSJérôme Duval 				control->nid,
7838c3da8ddSJérôme Duval 				VID_SET_AMPLIFIER_GAIN_MUTE,
7848c3da8ddSJérôme Duval 				(control->input ? AMP_SET_INPUT : AMP_SET_OUTPUT)
7858c3da8ddSJérôme Duval 				| AMP_SET_LEFT_CHANNEL
7868c3da8ddSJérôme Duval 				| AMP_SET_INPUT_INDEX(control->index)
7878c3da8ddSJérôme Duval 				| control->mute
78863d557f0SMichael Lotz 				| (resp[0] & AMP_GAIN_MASK));
7897848bf79SJérôme Duval 			TRACE("set_mix: sending verb to %" B_PRId32 ": %" B_PRIx32 " %"
7907848bf79SJérôme Duval 				B_PRIx32 " %x %lx\n", control->nid,
7918c3da8ddSJérôme Duval 				control->mute, resp[0] & AMP_GAIN_MASK, control->input,
7928c3da8ddSJérôme Duval 				(control->input ? AMP_SET_INPUT : AMP_SET_OUTPUT)
7938c3da8ddSJérôme Duval 				| AMP_SET_LEFT_CHANNEL
7948c3da8ddSJérôme Duval 				| AMP_SET_INPUT_INDEX(control->index)
7958c3da8ddSJérôme Duval 				| control->mute
79663d557f0SMichael Lotz 				| (resp[0] & AMP_GAIN_MASK));
7978c3da8ddSJérôme Duval 			verb[1] = MAKE_VERB(audioGroup->codec->addr,
7988c3da8ddSJérôme Duval 				control->nid,
7998c3da8ddSJérôme Duval 				VID_SET_AMPLIFIER_GAIN_MUTE,
8008c3da8ddSJérôme Duval 				(control->input ? AMP_SET_INPUT : AMP_SET_OUTPUT)
8018c3da8ddSJérôme Duval 				| AMP_SET_RIGHT_CHANNEL
8028c3da8ddSJérôme Duval 				| AMP_SET_INPUT_INDEX(control->index)
8038c3da8ddSJérôme Duval 				| control->mute
80463d557f0SMichael Lotz 				| (resp[1] & AMP_GAIN_MASK));
8057848bf79SJérôme Duval 			TRACE("set_mix: ctrl2 sending verb to %" B_PRId32 ": %" B_PRIx32
8067848bf79SJérôme Duval 				" %" B_PRIx32 " %x\n", control->nid, control->mute,
8077848bf79SJérôme Duval 				resp[1] & AMP_GAIN_MASK, control->input);
8088c3da8ddSJérôme Duval 			hda_send_verbs(audioGroup->codec, verb, NULL, 2);
8098d797bdaSAxel Dörfler 		} else if ((control->mix_control.flags & B_MULTI_MIX_GAIN) != 0) {
8108c3da8ddSJérôme Duval 			hda_multi_mixer_control *control2 = NULL;
8115076aaf0SJérôme Duval 			if (i+1<mmvi->item_count) {
8125076aaf0SJérôme Duval 				id = mmvi->values[i + 1].id - MULTI_CONTROL_FIRSTID;
8137848bf79SJérôme Duval 				if (id < 0 || id >= (int32)audioGroup->multi->control_count) {
8147848bf79SJérôme Duval 					dprintf("set_mix : invalid control id requested : %"
8157848bf79SJérôme Duval 						B_PRId32 "\n", id);
8168c3da8ddSJérôme Duval 				} else {
8178c3da8ddSJérôme Duval 					control2 = &audioGroup->multi->controls[id];
8188c3da8ddSJérôme Duval 					if (control2->mix_control.master != control->mix_control.id)
8198c3da8ddSJérôme Duval 						control2 = NULL;
8208c3da8ddSJérôme Duval 				}
8218c3da8ddSJérôme Duval 			}
8228c3da8ddSJérôme Duval 
8238d797bdaSAxel Dörfler 			if (control->mix_control.master == MULTI_CONTROL_MASTERID) {
824e4e70475SJérôme Duval 				control->gain = (uint32)(mmvi->values[i].gain
825e4e70475SJérôme Duval 					/ AMP_CAP_STEP_SIZE(control->capabilities)
8268c3da8ddSJérôme Duval 					+ AMP_CAP_OFFSET(control->capabilities));
8278d797bdaSAxel Dörfler 			}
8288c3da8ddSJérôme Duval 
8298d797bdaSAxel Dörfler 			if (control2
8308d797bdaSAxel Dörfler 				&& control2->mix_control.master != MULTI_CONTROL_MASTERID) {
831e4e70475SJérôme Duval 				control2->gain = (uint32)(mmvi->values[i+1].gain
832e4e70475SJérôme Duval 					/ AMP_CAP_STEP_SIZE(control2->capabilities)
8338c3da8ddSJérôme Duval 					+ AMP_CAP_OFFSET(control2->capabilities));
8348d797bdaSAxel Dörfler 			}
8357848bf79SJérôme Duval 			TRACE("set_mix: %" B_PRId32 " gain: %" B_PRIx32 " and %" B_PRId32
8367848bf79SJérôme Duval 				" gain: %" B_PRIx32 "\n", control->nid, control->gain,
8377848bf79SJérôme Duval 				control2->nid, control2->gain);
8388c3da8ddSJérôme Duval 			uint32 resp[2];
8398c3da8ddSJérôme Duval 			get_control_gain_mute(audioGroup, control, resp);
8408c3da8ddSJérôme Duval 			control->mute = resp[0] & AMP_MUTE;
8418c3da8ddSJérôme Duval 			if (control2)
8428c3da8ddSJérôme Duval 				control2->mute = resp[1] & AMP_MUTE;
8438c3da8ddSJérôme Duval 
8448c3da8ddSJérôme Duval 			uint32 verb[2];
8458c3da8ddSJérôme Duval 			verb[0] = MAKE_VERB(audioGroup->codec->addr,
8468c3da8ddSJérôme Duval 				control->nid,
8478c3da8ddSJérôme Duval 				VID_SET_AMPLIFIER_GAIN_MUTE,
8488c3da8ddSJérôme Duval 				(control->input ? AMP_SET_INPUT : AMP_SET_OUTPUT)
8498c3da8ddSJérôme Duval 				| AMP_SET_LEFT_CHANNEL
8508c3da8ddSJérôme Duval 				| AMP_SET_INPUT_INDEX(control->index)
8518c3da8ddSJérôme Duval 				| (control->mute & AMP_MUTE)
8528c3da8ddSJérôme Duval 				| (control->gain & AMP_GAIN_MASK));
8537848bf79SJérôme Duval 			TRACE("set_mix: sending verb to %" B_PRId32 ": %" B_PRIx32 " %"
8547848bf79SJérôme Duval 				B_PRIx32 " %x %lx\n", control->nid,
8558c3da8ddSJérôme Duval 				control->mute, control->gain, control->input,
8568c3da8ddSJérôme Duval 				(control->input ? AMP_SET_INPUT : AMP_SET_OUTPUT)
8578c3da8ddSJérôme Duval 				| AMP_SET_LEFT_CHANNEL
8588c3da8ddSJérôme Duval 				| AMP_SET_INPUT_INDEX(control->index)
8598c3da8ddSJérôme Duval 				| (control->mute & AMP_MUTE)
8608c3da8ddSJérôme Duval 				| (control->gain & AMP_GAIN_MASK));
8618c3da8ddSJérôme Duval 			if (control2) {
8628c3da8ddSJérôme Duval 				verb[1] = MAKE_VERB(audioGroup->codec->addr,
8638c3da8ddSJérôme Duval 					control2->nid,
8648c3da8ddSJérôme Duval 					VID_SET_AMPLIFIER_GAIN_MUTE,
8658c3da8ddSJérôme Duval 					(control->input ? AMP_SET_INPUT : AMP_SET_OUTPUT)
8668c3da8ddSJérôme Duval 					| AMP_SET_RIGHT_CHANNEL
8678c3da8ddSJérôme Duval 					| AMP_SET_INPUT_INDEX(control->index)
8688c3da8ddSJérôme Duval 					| (control2->mute & AMP_MUTE)
8698c3da8ddSJérôme Duval 					| (control2->gain & AMP_GAIN_MASK));
8707848bf79SJérôme Duval 				TRACE("set_mix: ctrl2 sending verb to %" B_PRId32 ": %"
8717848bf79SJérôme Duval 					B_PRIx32 " %" B_PRIx32 " %x\n", control2->nid,
8727848bf79SJérôme Duval 					control2->mute, control2->gain, control2->input);
8738c3da8ddSJérôme Duval 			}
8748c3da8ddSJérôme Duval 			hda_send_verbs(audioGroup->codec, verb, NULL, control2 ? 2 : 1);
8758c3da8ddSJérôme Duval 
8768c3da8ddSJérôme Duval 			if (control2)
8778c3da8ddSJérôme Duval 				i++;
8788d797bdaSAxel Dörfler 		} else if ((control->mix_control.flags & B_MIX_MUX_MIXER) != 0) {
8797848bf79SJérôme Duval 			TRACE("set_mix: %" B_PRId32 " mixer: %" B_PRIu32 "\n",
8807848bf79SJérôme Duval 				control->nid, mmvi->values[i].mux);
881e4e70475SJérôme Duval 			hda_widget *mixer = hda_audio_group_get_widget(audioGroup,
882e4e70475SJérôme Duval 				control->nid);
883ba4d7ed2SJérôme Duval 			uint32 verb[mixer->num_inputs];
884ba4d7ed2SJérôme Duval 			for (uint32 j = 0; j < mixer->num_inputs; j++) {
885ba4d7ed2SJérôme Duval 				verb[j] = MAKE_VERB(audioGroup->codec->addr,
886ba4d7ed2SJérôme Duval 					control->nid, VID_SET_AMPLIFIER_GAIN_MUTE, AMP_SET_INPUT
887e4e70475SJérôme Duval 					| AMP_SET_LEFT_CHANNEL | AMP_SET_RIGHT_CHANNEL
888e4e70475SJérôme Duval 					| AMP_SET_INPUT_INDEX(j)
889ba4d7ed2SJérôme Duval 					| ((mmvi->values[i].mux == j) ? 0 : AMP_MUTE));
8907848bf79SJérôme Duval 				TRACE("set_mix: %" B_PRId32 " mixer %smuting %" B_PRIu32 " (%"
8917848bf79SJérôme Duval 					B_PRIu32 ")\n", control->nid,
892ba4d7ed2SJérôme Duval 					(mmvi->values[i].mux == j) ? "un" : "", j, verb[j]);
8938c3da8ddSJérôme Duval 			}
894e4e70475SJérôme Duval 			if (hda_send_verbs(audioGroup->codec, verb, NULL, mixer->num_inputs)
895e4e70475SJérôme Duval 				!= B_OK)
8967848bf79SJérôme Duval 				dprintf("hda: Setting mixer %" B_PRId32 " failed on widget %"
8977848bf79SJérôme Duval 					B_PRIu32 "!\n", mmvi->values[i].mux, control->nid);
8988d797bdaSAxel Dörfler 		} else if ((control->mix_control.flags & B_MIX_MUX_SELECTOR) != 0) {
899ba4d7ed2SJérôme Duval 			uint32 verb = MAKE_VERB(audioGroup->codec->addr, control->nid,
900ba4d7ed2SJérôme Duval 				VID_SET_CONNECTION_SELECT, mmvi->values[i].mux);
9018d797bdaSAxel Dörfler 			if (hda_send_verbs(audioGroup->codec, &verb, NULL, 1) != B_OK) {
9027848bf79SJérôme Duval 				dprintf("hda: Setting output selector %" B_PRId32 " failed on "
9037848bf79SJérôme Duval 					"widget %" B_PRIu32 "!\n", mmvi->values[i].mux,
9047848bf79SJérôme Duval 					control->nid);
9058d797bdaSAxel Dörfler 			}
9067848bf79SJérôme Duval 			TRACE("set_mix: %" B_PRId32 " selector: %" B_PRIu32 "\n",
9077848bf79SJérôme Duval 				control->nid, mmvi->values[i].mux);
908ba4d7ed2SJérôme Duval 		}
9098c3da8ddSJérôme Duval 	}
9108c3da8ddSJérôme Duval 	return B_OK;
9118c3da8ddSJérôme Duval }
9128c3da8ddSJérôme Duval 
9138c3da8ddSJérôme Duval 
9140bd93b7dSStephan Aßmus static uint32
default_buffer_length_for_rate(uint32 rate)9150bd93b7dSStephan Aßmus default_buffer_length_for_rate(uint32 rate)
9160bd93b7dSStephan Aßmus {
9173d5a11eeSFrançois Revol 	// keep the latency about the same as 2048 frames per buffer at 44100 Hz
9180bd93b7dSStephan Aßmus 	switch (rate) {
9190bd93b7dSStephan Aßmus 	case B_SR_8000:
9200bd93b7dSStephan Aßmus 		return 512;
9210bd93b7dSStephan Aßmus 	case B_SR_11025:
9220bd93b7dSStephan Aßmus 		return 512;
9230bd93b7dSStephan Aßmus 	case B_SR_16000:
9240bd93b7dSStephan Aßmus 		return 1024;
9250bd93b7dSStephan Aßmus 	case B_SR_22050:
9260bd93b7dSStephan Aßmus 		return 1024;
9270bd93b7dSStephan Aßmus 	case B_SR_32000:
9280bd93b7dSStephan Aßmus 		return 2048;
9290bd93b7dSStephan Aßmus 	case B_SR_44100:
9300bd93b7dSStephan Aßmus 		return 2048;
9310bd93b7dSStephan Aßmus 	case B_SR_48000:
9320bd93b7dSStephan Aßmus 		return 2048;
9330bd93b7dSStephan Aßmus 	case B_SR_88200:
9340bd93b7dSStephan Aßmus 		return 4096;
9350bd93b7dSStephan Aßmus 	case B_SR_96000:
9360bd93b7dSStephan Aßmus 		return 6144;
9370bd93b7dSStephan Aßmus 	case B_SR_176400:
9380bd93b7dSStephan Aßmus 		return 8192;
9390bd93b7dSStephan Aßmus 	case B_SR_192000:
9400bd93b7dSStephan Aßmus 		return 10240;
9410bd93b7dSStephan Aßmus 	case B_SR_384000:
9420bd93b7dSStephan Aßmus 		return 16384;
9430bd93b7dSStephan Aßmus 	}
9440bd93b7dSStephan Aßmus 	return 2048;
9450bd93b7dSStephan Aßmus };
9460bd93b7dSStephan Aßmus 
9470bd93b7dSStephan Aßmus 
948340dd4feSAxel Dörfler static status_t
get_buffers(hda_audio_group * audioGroup,multi_buffer_list * data)949340dd4feSAxel Dörfler get_buffers(hda_audio_group* audioGroup, multi_buffer_list* data)
950340dd4feSAxel Dörfler {
9518aea77d0SHumdinger 	if (requested_settings.play_buffer_frames != 0)
9528aea77d0SHumdinger 		data->request_playback_buffer_size = requested_settings.play_buffer_frames;
9538aea77d0SHumdinger 
9548aea77d0SHumdinger 	if (requested_settings.play_buffer_count != 0)
9558aea77d0SHumdinger 		data->request_playback_buffers = requested_settings.play_buffer_count;
9568aea77d0SHumdinger 
9578aea77d0SHumdinger 	if (requested_settings.record_buffer_frames != 0)
9588aea77d0SHumdinger 		data->request_record_buffer_size = requested_settings.record_buffer_frames;
9598aea77d0SHumdinger 
9608aea77d0SHumdinger 	if (requested_settings.record_buffer_count != 0)
9618aea77d0SHumdinger 		data->request_record_buffers = requested_settings.record_buffer_count;
9628aea77d0SHumdinger 
9637848bf79SJérôme Duval 	TRACE("playback: %" B_PRId32 " buffers, %" B_PRId32 " channels, %" B_PRIu32
9647848bf79SJérôme Duval 		" samples\n", data->request_playback_buffers,
9657848bf79SJérôme Duval 		data->request_playback_channels, data->request_playback_buffer_size);
9667848bf79SJérôme Duval 	TRACE("record: %" B_PRId32 " buffers, %" B_PRId32 " channels, %" B_PRIu32
9677848bf79SJérôme Duval 		" samples\n", data->request_record_buffers,
9687848bf79SJérôme Duval 		data->request_record_channels, data->request_record_buffer_size);
969340dd4feSAxel Dörfler 
970340dd4feSAxel Dörfler 	/* Determine what buffers we return given the request */
971340dd4feSAxel Dörfler 
972340dd4feSAxel Dörfler 	data->return_playback_buffers = data->request_playback_buffers;
973340dd4feSAxel Dörfler 	data->return_playback_channels = data->request_playback_channels;
974340dd4feSAxel Dörfler 	data->return_playback_buffer_size = data->request_playback_buffer_size;
975340dd4feSAxel Dörfler 	data->return_record_buffers = data->request_record_buffers;
976340dd4feSAxel Dörfler 	data->return_record_channels = data->request_record_channels;
977340dd4feSAxel Dörfler 	data->return_record_buffer_size = data->request_record_buffer_size;
978340dd4feSAxel Dörfler 
979340dd4feSAxel Dörfler 	/* Workaround for Haiku multi_audio API, since it prefers to let the
980340dd4feSAxel Dörfler 	   driver pick values, while the BeOS multi_audio actually gives the
981340dd4feSAxel Dörfler 	   user's defaults. */
982340dd4feSAxel Dörfler 	if (data->return_playback_buffers > STREAM_MAX_BUFFERS
983340dd4feSAxel Dörfler 		|| data->return_playback_buffers < STREAM_MIN_BUFFERS)
984340dd4feSAxel Dörfler 		data->return_playback_buffers = STREAM_MIN_BUFFERS;
985340dd4feSAxel Dörfler 
986340dd4feSAxel Dörfler 	if (data->return_record_buffers > STREAM_MAX_BUFFERS
987340dd4feSAxel Dörfler 		|| data->return_record_buffers < STREAM_MIN_BUFFERS)
988340dd4feSAxel Dörfler 		data->return_record_buffers = STREAM_MIN_BUFFERS;
989340dd4feSAxel Dörfler 
99025a627d8SJerome Duval 	if (data->return_playback_buffer_size == 0
99125a627d8SJerome Duval 		&& audioGroup->playback_stream != NULL) {
9920bd93b7dSStephan Aßmus 		data->return_playback_buffer_size = default_buffer_length_for_rate(
9930bd93b7dSStephan Aßmus 			audioGroup->playback_stream->sample_rate);
9940bd93b7dSStephan Aßmus 	}
995340dd4feSAxel Dörfler 
99625a627d8SJerome Duval 	if (data->return_record_buffer_size == 0
99725a627d8SJerome Duval 		&& audioGroup->record_stream != NULL) {
9980bd93b7dSStephan Aßmus 		data->return_record_buffer_size = default_buffer_length_for_rate(
9990bd93b7dSStephan Aßmus 				audioGroup->record_stream->sample_rate);
10000bd93b7dSStephan Aßmus 	}
1001340dd4feSAxel Dörfler 
1002340dd4feSAxel Dörfler 	/* ... from here on, we can assume again that a reasonable request is
1003340dd4feSAxel Dörfler 	   being made */
1004340dd4feSAxel Dörfler 
10051fb04337SJérôme Duval 	data->flags = B_MULTI_BUFFER_PLAYBACK | B_MULTI_BUFFER_RECORD;
1006340dd4feSAxel Dörfler 
1007340dd4feSAxel Dörfler 	/* Copy the settings into the streams */
100830f55bc9SAxel Dörfler 
100930f55bc9SAxel Dörfler 	if (audioGroup->playback_stream != NULL) {
1010340dd4feSAxel Dörfler 		audioGroup->playback_stream->num_buffers = data->return_playback_buffers;
1011340dd4feSAxel Dörfler 		audioGroup->playback_stream->num_channels = data->return_playback_channels;
1012340dd4feSAxel Dörfler 		audioGroup->playback_stream->buffer_length
1013340dd4feSAxel Dörfler 			= data->return_playback_buffer_size;
1014340dd4feSAxel Dörfler 
1015340dd4feSAxel Dörfler 		status_t status = hda_stream_setup_buffers(audioGroup,
1016340dd4feSAxel Dörfler 			audioGroup->playback_stream, "Playback");
1017340dd4feSAxel Dörfler 		if (status != B_OK) {
1018340dd4feSAxel Dörfler 			dprintf("hda: Error setting up playback buffers: %s\n",
1019340dd4feSAxel Dörfler 				strerror(status));
1020340dd4feSAxel Dörfler 			return status;
1021340dd4feSAxel Dörfler 		}
102230f55bc9SAxel Dörfler 	}
1023340dd4feSAxel Dörfler 
102430f55bc9SAxel Dörfler 	if (audioGroup->record_stream != NULL) {
1025340dd4feSAxel Dörfler 		audioGroup->record_stream->num_buffers = data->return_record_buffers;
1026340dd4feSAxel Dörfler 		audioGroup->record_stream->num_channels = data->return_record_channels;
1027340dd4feSAxel Dörfler 		audioGroup->record_stream->buffer_length
1028340dd4feSAxel Dörfler 			= data->return_record_buffer_size;
1029340dd4feSAxel Dörfler 
103030f55bc9SAxel Dörfler 		status_t status = hda_stream_setup_buffers(audioGroup,
103130f55bc9SAxel Dörfler 			audioGroup->record_stream, "Recording");
1032340dd4feSAxel Dörfler 		if (status != B_OK) {
1033340dd4feSAxel Dörfler 			dprintf("hda: Error setting up recording buffers: %s\n",
1034340dd4feSAxel Dörfler 				strerror(status));
1035340dd4feSAxel Dörfler 			return status;
1036340dd4feSAxel Dörfler 		}
103730f55bc9SAxel Dörfler 	}
1038340dd4feSAxel Dörfler 
1039340dd4feSAxel Dörfler 	/* Setup data structure for multi_audio API... */
1040340dd4feSAxel Dörfler 
104130f55bc9SAxel Dörfler 	if (audioGroup->playback_stream != NULL) {
1042340dd4feSAxel Dörfler 		uint32 playbackSampleSize = audioGroup->playback_stream->sample_size;
1043340dd4feSAxel Dörfler 
1044340dd4feSAxel Dörfler 		for (int32 i = 0; i < data->return_playback_buffers; i++) {
10454f2fac06SJérôme Duval 			struct buffer_desc descs[data->return_playback_channels];
1046340dd4feSAxel Dörfler 			for (int32 channelIndex = 0;
1047340dd4feSAxel Dörfler 					channelIndex < data->return_playback_channels; channelIndex++) {
10484f2fac06SJérôme Duval 				descs[channelIndex].base = (char*)audioGroup->playback_stream->buffers[i]
1049340dd4feSAxel Dörfler 					+ playbackSampleSize * channelIndex;
10504f2fac06SJérôme Duval 				descs[channelIndex].stride = playbackSampleSize
10514f2fac06SJérôme Duval 					* data->return_playback_channels;
10524f2fac06SJérôme Duval 			}
10534f2fac06SJérôme Duval 			if (!IS_USER_ADDRESS(data->playback_buffers[i])
10544f2fac06SJérôme Duval 				|| user_memcpy(data->playback_buffers[i], descs, sizeof(descs))
10554f2fac06SJérôme Duval 				< B_OK) {
10564f2fac06SJérôme Duval 				return B_BAD_ADDRESS;
1057340dd4feSAxel Dörfler 			}
1058340dd4feSAxel Dörfler 		}
105930f55bc9SAxel Dörfler 	}
106030f55bc9SAxel Dörfler 
106130f55bc9SAxel Dörfler 	if (audioGroup->record_stream != NULL) {
106230f55bc9SAxel Dörfler 		uint32 recordSampleSize = audioGroup->record_stream->sample_size;
1063340dd4feSAxel Dörfler 
1064340dd4feSAxel Dörfler 		for (int32 i = 0; i < data->return_record_buffers; i++) {
10654f2fac06SJérôme Duval 			struct buffer_desc descs[data->return_record_channels];
1066340dd4feSAxel Dörfler 			for (int32 channelIndex = 0;
1067340dd4feSAxel Dörfler 					channelIndex < data->return_record_channels; channelIndex++) {
10684f2fac06SJérôme Duval 				descs[channelIndex].base = (char*)audioGroup->record_stream->buffers[i]
1069340dd4feSAxel Dörfler 					+ recordSampleSize * channelIndex;
10704f2fac06SJérôme Duval 				descs[channelIndex].stride = recordSampleSize
10714f2fac06SJérôme Duval 					* data->return_record_channels;
10724f2fac06SJérôme Duval 			}
10734f2fac06SJérôme Duval 			if (!IS_USER_ADDRESS(data->record_buffers[i])
10744f2fac06SJérôme Duval 				|| user_memcpy(data->record_buffers[i], descs, sizeof(descs))
10754f2fac06SJérôme Duval 				< B_OK) {
10764f2fac06SJérôme Duval 				return B_BAD_ADDRESS;
1077340dd4feSAxel Dörfler 			}
1078340dd4feSAxel Dörfler 		}
107930f55bc9SAxel Dörfler 	}
1080340dd4feSAxel Dörfler 
1081340dd4feSAxel Dörfler 	return B_OK;
1082340dd4feSAxel Dörfler }
1083340dd4feSAxel Dörfler 
1084340dd4feSAxel Dörfler 
1085340dd4feSAxel Dörfler /*! playback_buffer_cycle is the buffer we want to have played */
1086340dd4feSAxel Dörfler static status_t
buffer_exchange(hda_audio_group * audioGroup,multi_buffer_info * data)1087340dd4feSAxel Dörfler buffer_exchange(hda_audio_group* audioGroup, multi_buffer_info* data)
1088340dd4feSAxel Dörfler {
1089340dd4feSAxel Dörfler 	cpu_status status;
10901fb04337SJérôme Duval 	status_t err;
10917f2d1a26SJérôme Duval 	multi_buffer_info buffer_info;
1092340dd4feSAxel Dörfler 
109330f55bc9SAxel Dörfler 	if (audioGroup->playback_stream == NULL)
109430f55bc9SAxel Dörfler 		return B_ERROR;
109530f55bc9SAxel Dörfler 
1096340dd4feSAxel Dörfler 	if (!audioGroup->playback_stream->running) {
1097340dd4feSAxel Dörfler 		hda_stream_start(audioGroup->codec->controller,
1098340dd4feSAxel Dörfler 			audioGroup->playback_stream);
1099340dd4feSAxel Dörfler 	}
11001fb04337SJérôme Duval 	if (audioGroup->record_stream && !audioGroup->record_stream->running) {
11011fb04337SJérôme Duval 		hda_stream_start(audioGroup->codec->controller,
11021fb04337SJérôme Duval 			audioGroup->record_stream);
11031fb04337SJérôme Duval 	}
1104340dd4feSAxel Dörfler 
11057f2d1a26SJérôme Duval #ifdef __HAIKU__
11067f2d1a26SJérôme Duval 	if (user_memcpy(&buffer_info, data, sizeof(buffer_info)) < B_OK)
11077f2d1a26SJérôme Duval 		return B_BAD_ADDRESS;
11087f2d1a26SJérôme Duval #else
11097f2d1a26SJérôme Duval 	memcpy(&buffer_info, data, sizeof(buffer_info));
11107f2d1a26SJérôme Duval #endif
11117f2d1a26SJérôme Duval 
1112340dd4feSAxel Dörfler 	/* do playback */
1113d936f5e3SJérôme Duval 	err = acquire_sem_etc(audioGroup->codec->controller->buffer_ready_sem,
1114340dd4feSAxel Dörfler 		1, B_CAN_INTERRUPT, 0);
11151fb04337SJérôme Duval 	if (err != B_OK) {
11169457b769SAdrien Destugues 		ERROR("%s: Error waiting for playback buffer to finish (%s)!\n", __func__,
11171fb04337SJérôme Duval 			strerror(err));
11181fb04337SJérôme Duval 		return err;
1119340dd4feSAxel Dörfler 	}
1120340dd4feSAxel Dörfler 
1121340dd4feSAxel Dörfler 	status = disable_interrupts();
1122340dd4feSAxel Dörfler 	acquire_spinlock(&audioGroup->playback_stream->lock);
1123340dd4feSAxel Dörfler 
11248d797bdaSAxel Dörfler 	buffer_info.playback_buffer_cycle
11258d797bdaSAxel Dörfler 		= (audioGroup->playback_stream->buffer_cycle)
11268d797bdaSAxel Dörfler 			% audioGroup->playback_stream->num_buffers;
11277f2d1a26SJérôme Duval 	buffer_info.played_real_time = audioGroup->playback_stream->real_time;
11287f2d1a26SJérôme Duval 	buffer_info.played_frames_count = audioGroup->playback_stream->frames_count;
1129340dd4feSAxel Dörfler 
1130340dd4feSAxel Dörfler 	release_spinlock(&audioGroup->playback_stream->lock);
11311fb04337SJérôme Duval 
11321fb04337SJérôme Duval 	if (audioGroup->record_stream) {
11331fb04337SJérôme Duval 		acquire_spinlock(&audioGroup->record_stream->lock);
11348d797bdaSAxel Dörfler 		buffer_info.record_buffer_cycle
11358d797bdaSAxel Dörfler 			= (audioGroup->record_stream->buffer_cycle - 1)
11368d797bdaSAxel Dörfler 				% audioGroup->record_stream->num_buffers;
11377f2d1a26SJérôme Duval 		buffer_info.recorded_real_time = audioGroup->record_stream->real_time;
11388d797bdaSAxel Dörfler 		buffer_info.recorded_frames_count
11398d797bdaSAxel Dörfler 			= audioGroup->record_stream->frames_count;
11401fb04337SJérôme Duval 		release_spinlock(&audioGroup->record_stream->lock);
11411fb04337SJérôme Duval 	}
11421fb04337SJérôme Duval 
1143340dd4feSAxel Dörfler 	restore_interrupts(status);
1144340dd4feSAxel Dörfler 
1145b8ea3ce7SJérôme Duval #ifdef __HAIKU__
11467f2d1a26SJérôme Duval 	if (user_memcpy(data, &buffer_info, sizeof(buffer_info)) < B_OK)
11477f2d1a26SJérôme Duval 		return B_BAD_ADDRESS;
1148b8ea3ce7SJérôme Duval #else
11497f2d1a26SJérôme Duval 	memcpy(data, &buffer_info, sizeof(buffer_info));
1150b8ea3ce7SJérôme Duval #endif
1151b8ea3ce7SJérôme Duval 
11523601f762SAxel Dörfler #if 0
11538d797bdaSAxel Dörfler 	static int debugBuffersExchanged = 0;
11543601f762SAxel Dörfler 
11558d797bdaSAxel Dörfler 	debugBuffersExchanged++;
11568d797bdaSAxel Dörfler 	if ((debugBuffersExchanged % 100) == 1 && debugBuffersExchanged < 1111)
11578d797bdaSAxel Dörfler 		dprintf("%s: %d buffers processed\n", __func__, debugBuffersExchanged);
11583601f762SAxel Dörfler #endif
1159340dd4feSAxel Dörfler 	return B_OK;
1160340dd4feSAxel Dörfler }
1161340dd4feSAxel Dörfler 
1162340dd4feSAxel Dörfler 
1163340dd4feSAxel Dörfler static status_t
buffer_force_stop(hda_audio_group * audioGroup)1164340dd4feSAxel Dörfler buffer_force_stop(hda_audio_group* audioGroup)
1165340dd4feSAxel Dörfler {
116630f55bc9SAxel Dörfler 	if (audioGroup->playback_stream != NULL) {
116730f55bc9SAxel Dörfler 		hda_stream_stop(audioGroup->codec->controller,
116830f55bc9SAxel Dörfler 			audioGroup->playback_stream);
116930f55bc9SAxel Dörfler 	}
11701fb04337SJérôme Duval 	if (audioGroup->record_stream != NULL) {
11711fb04337SJérôme Duval 		hda_stream_stop(audioGroup->codec->controller,
11721fb04337SJérôme Duval 			audioGroup->record_stream);
11731fb04337SJérôme Duval 	}
1174340dd4feSAxel Dörfler 
1175340dd4feSAxel Dörfler 	return B_OK;
1176340dd4feSAxel Dörfler }
1177340dd4feSAxel Dörfler 
1178340dd4feSAxel Dörfler 
11794f2fac06SJérôme Duval #define cookie_type hda_audio_group
11804f2fac06SJérôme Duval #include "../generic/multi.c"
11814f2fac06SJérôme Duval 
11824f2fac06SJérôme Duval 
1183340dd4feSAxel Dörfler status_t
multi_audio_control(void * cookie,uint32 op,void * arg,size_t len)1184340dd4feSAxel Dörfler multi_audio_control(void* cookie, uint32 op, void* arg, size_t len)
1185340dd4feSAxel Dörfler {
1186340dd4feSAxel Dörfler 	hda_codec* codec = (hda_codec*)cookie;
1187340dd4feSAxel Dörfler 	hda_audio_group* audioGroup;
1188340dd4feSAxel Dörfler 
1189340dd4feSAxel Dörfler 	/* FIXME: We should simply pass the audioGroup into here... */
1190340dd4feSAxel Dörfler 	if (!codec || codec->num_audio_groups == 0)
1191340dd4feSAxel Dörfler 		return ENODEV;
1192340dd4feSAxel Dörfler 
1193340dd4feSAxel Dörfler 	audioGroup = codec->audio_groups[0];
1194340dd4feSAxel Dörfler 
11954f2fac06SJérôme Duval 	return multi_audio_control_generic(audioGroup, op, arg, len);
1196340dd4feSAxel Dörfler }
1197