From 30f55bc9908b08803b0fdd7f9f10e3a9f132a63f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Thu, 13 Mar 2008 12:25:29 +0000 Subject: [PATCH] * Rewrote widget output path discovery, it's now very similar to what the FreeBSD driver is doing. * hda_codec_new_audio_group() did not free the audio group's widgets on failure. * No longer create the input stream for now. * Reworked multiaudio-support to work regardless if there is only a playback or record stream. * With these changes, I hear nothing on my laptop anymore (before there was noise), but on another system, I can finally hear something that sounds very much like the sinus wave the multi_audio_test application produces; the sound quality is pretty bad though (lots of periodical noise and glitches). * Made B_MULTI_GET_DESCRIPTION safe to be called from userland. * Minor other cleanup. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24383 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/kernel/drivers/audio/hda/driver.h | 17 +- .../kernel/drivers/audio/hda/hda_codec.cpp | 361 ++++++++++-------- .../kernel/drivers/audio/hda/hda_codec_defs.h | 2 +- .../drivers/audio/hda/hda_controller.cpp | 29 +- .../drivers/audio/hda/hda_multi_audio.cpp | 179 ++++++--- 5 files changed, 356 insertions(+), 232 deletions(-) diff --git a/src/add-ons/kernel/drivers/audio/hda/driver.h b/src/add-ons/kernel/drivers/audio/hda/driver.h index 957f7b438b..20af08f598 100644 --- a/src/add-ons/kernel/drivers/audio/hda/driver.h +++ b/src/add-ons/kernel/drivers/audio/hda/driver.h @@ -37,6 +37,7 @@ #define HDA_MAX_STREAMS 16 #define MAX_CODEC_RESPONSES 10 #define MAX_INPUTS 32 +#define MAX_IO_WIDGETS 8 /* FIXME: Find out why we need so much! */ #define DEFAULT_FRAMES_PER_BUFFER 4096 @@ -62,7 +63,8 @@ struct hda_stream { uint32 type; uint32 pin_widget; /* PIN Widget ID */ - uint32 io_widget; /* Input/Output Converter Widget ID */ + uint32 io_widgets[MAX_IO_WIDGETS]; /* Input/Output Converter Widget ID */ + uint32 num_io_widgets; uint32 sample_rate; uint32 sample_format; @@ -108,11 +110,7 @@ struct hda_widget { struct { uint32 formats; uint32 rates; - } output; - struct { - uint32 formats; - uint32 rates; - } input; + } io; struct { } mixer; struct { @@ -123,6 +121,9 @@ struct hda_widget { } d; }; +#define WIDGET_FLAG_OUTPUT_PATH 0x01 +#define WIDGET_FLAG_INPUT_PATH 0x02 + /*! This structure describes a single Audio Function Group. An AFG is a group of audio widgets which can be used to configure multiple streams of audio either from the HDA Link to an output device (= playback) @@ -215,6 +216,8 @@ extern hda_controller gCards[MAX_CARDS]; extern uint32 gNumCards; /* hda_codec.c */ +status_t hda_audio_group_get_widgets(hda_audio_group* audioGroup, + hda_stream* stream); hda_codec* hda_codec_new(hda_controller* controller, uint32 cad); void hda_codec_delete(hda_codec* codec); @@ -229,7 +232,7 @@ status_t hda_send_verbs(hda_codec* codec, corb_t* verbs, uint32* responses, uint32 count); /* hda_controller.c: Stream support */ -hda_stream* hda_stream_new(hda_controller* controller, int type); +hda_stream* hda_stream_new(hda_audio_group* audioGroup, int type); void hda_stream_delete(hda_stream* stream); status_t hda_stream_setup_buffers(hda_audio_group* audioGroup, hda_stream* stream, const char* desc); diff --git a/src/add-ons/kernel/drivers/audio/hda/hda_codec.cpp b/src/add-ons/kernel/drivers/audio/hda/hda_codec.cpp index 9ec54a44e4..1d5e5d99d8 100644 --- a/src/add-ons/kernel/drivers/audio/hda/hda_codec.cpp +++ b/src/add-ons/kernel/drivers/audio/hda/hda_codec.cpp @@ -248,13 +248,13 @@ hda_widget_get_stream_support(hda_audio_group* audioGroup, hda_widget* widget) { if ((widget->capabilities.audio & AUDIO_CAP_FORMAT_OVERRIDE) == 0) { // adopt capabilities of the audio group - widget->d.output.formats = audioGroup->supported_formats; - widget->d.output.rates = audioGroup->supported_rates; + widget->d.io.formats = audioGroup->supported_formats; + widget->d.io.rates = audioGroup->supported_rates; return B_OK; } return hda_get_stream_support(audioGroup->codec, widget->node_id, - &widget->d.output.formats, &widget->d.output.rates); + &widget->d.io.formats, &widget->d.io.rates); } @@ -303,6 +303,17 @@ hda_widget_get_amplifier_capabilities(hda_audio_group* audioGroup, } +static hda_widget* +hda_audio_group_get_widget(hda_audio_group* audioGroup, uint32 nodeID) +{ + if (audioGroup->widget_start > nodeID + || audioGroup->widget_start + audioGroup->widget_count < nodeID) + return NULL; + + return &audioGroup->widgets[nodeID - audioGroup->widget_start]; +} + + static status_t hda_widget_get_connections(hda_audio_group* audioGroup, hda_widget* widget) { @@ -378,7 +389,7 @@ hda_widget_get_connections(hda_audio_group* audioGroup, hda_widget* widget) widget->num_inputs = numInputs; if (widget->num_inputs == 1) - widget->active_input = widget->inputs[0]; + widget->active_input = 0; return B_OK; } @@ -403,7 +414,7 @@ hda_codec_parse_audio_group(hda_audio_group* audioGroup) verbs[1] = MAKE_VERB(audioGroup->codec->addr, audioGroup->root_node_id, VID_GET_PARAMETER, PID_GPIO_COUNT); verbs[2] = MAKE_VERB(audioGroup->codec->addr, audioGroup->root_node_id, - VID_GET_PARAMETER, PID_SUBORDINATE_NODE_COUNT); + VID_GET_PARAMETER, PID_SUB_NODE_COUNT); if (hda_send_verbs(audioGroup->codec, verbs, resp, 3) != B_OK) return B_ERROR; @@ -511,51 +522,123 @@ hda_codec_parse_audio_group(hda_audio_group* audioGroup) } -/*! Find path from 'widget' to a widget of type \a widgetType, returning its - widget id. - Returns 0 if not found. -*/ -static uint32 -hda_codec_audio_group_find_path(hda_audio_group* audioGroup, uint32 widget, - hda_widget_type widgetType, uint32 depth) +/*! Find output path for widget */ +static bool +hda_widget_find_output_path(hda_audio_group* audioGroup, hda_widget* widget, + uint32 depth) { - int groupIndex = widget - audioGroup->widget_start; + if (widget == NULL || depth > 16) + return false; + + switch (widget->type) { + case WT_AUDIO_OUTPUT: + widget->flags |= WIDGET_FLAG_OUTPUT_PATH; +dprintf(" %*soutput: added output widget %ld\n", (int)depth * 2, "", widget->node_id); + return true; - switch (audioGroup->widgets[groupIndex].type) { case WT_AUDIO_MIXER: - for (uint32 i = 0; i < audioGroup->widgets[groupIndex].num_inputs; i++) { - if (hda_codec_audio_group_find_path(audioGroup, - audioGroup->widgets[groupIndex].inputs[i], widgetType, - depth + 1)) { - if (audioGroup->widgets[groupIndex].active_input == -1) - audioGroup->widgets[groupIndex].active_input = i; - - return audioGroup->widgets[groupIndex].inputs[i]; - } - } - break; - case WT_AUDIO_SELECTOR: { - int32 i = audioGroup->widgets[groupIndex].active_input; - if (i != -1) { - widget = audioGroup->widgets[groupIndex].inputs[i]; - if (hda_codec_audio_group_find_path(audioGroup, widget, - widgetType, depth + 1)) { - return widget; + // search for output in this path + bool found = false; + for (uint32 i = 0; i < widget->num_inputs; i++) { + hda_widget* inputWidget = hda_audio_group_get_widget(audioGroup, + widget->inputs[i]); + + if (hda_widget_find_output_path(audioGroup, inputWidget, + depth + 1)) { + if (widget->active_input == -1) + widget->active_input = i; + + widget->flags |= WIDGET_FLAG_OUTPUT_PATH; +dprintf(" %*soutput: added mixer/selector widget %ld\n", (int)depth * 2, "", widget->node_id); + found = true; } } - break; +if (!found) dprintf(" %*soutput: not added mixer/selector widget %ld\n", (int)depth * 2, "", widget->node_id); + return found; } default: - if (audioGroup->widgets[groupIndex].type == widgetType) - return widget; + return false; + } +} - break; + +static bool +hda_audio_group_build_output_tree(hda_audio_group* audioGroup, bool useMixer) +{ + bool found = false; + +dprintf("build output tree: %suse mixer\n", useMixer ? "" : "don't "); + for (uint32 i = 0; i < audioGroup->widget_count; i++) { + hda_widget& widget = audioGroup->widgets[i]; + + if (widget.type != WT_PIN_COMPLEX || !widget.d.pin.output + || (widget.d.pin.device != PIN_DEV_HEAD_PHONE_OUT + && widget.d.pin.device != PIN_DEV_SPEAKER + && widget.d.pin.device != PIN_DEV_LINE_OUT)) + continue; + +dprintf(" look at pin widget %ld (%ld inputs)\n", widget.node_id, widget.num_inputs); + for (uint32 j = 0; j < widget.num_inputs; j++) { + hda_widget* inputWidget = hda_audio_group_get_widget(audioGroup, + widget.inputs[j]); +dprintf(" try widget %ld: %p\n", widget.inputs[j], inputWidget); + if (inputWidget == NULL) + continue; + + if (useMixer && inputWidget->type != WT_AUDIO_MIXER + && inputWidget->type != WT_AUDIO_SELECTOR) + continue; +dprintf(" widget %ld is candidate\n", inputWidget->node_id); + + if (hda_widget_find_output_path(audioGroup, inputWidget, 0)) { +dprintf(" add pin widget %ld\n", widget.node_id); + if (widget.active_input == -1) + widget.active_input = j; + widget.flags |= WIDGET_FLAG_OUTPUT_PATH; + found = true; + } + } } - return 0; + return found; +} + + +static status_t +hda_audio_group_build_tree(hda_audio_group* audioGroup) +{ + if (!hda_audio_group_build_output_tree(audioGroup, true)) { + // didn't find a mixer path, try again without +dprintf("try without mixer!\n"); + if (!hda_audio_group_build_output_tree(audioGroup, false)) + return ENODEV; + } + +dprintf("build tree!\n"); + // TODO: input path! + + // select active connections + + for (uint32 i = 0; i < audioGroup->widget_count; i++) { + hda_widget& widget = audioGroup->widgets[i]; + + if (widget.active_input == -1) + widget.active_input = 0; + if (widget.num_inputs < 2) + continue; + + corb_t verb = MAKE_VERB(audioGroup->codec->addr, + widget.node_id, VID_SET_CONNECTION_SELECT, widget.active_input); + if (hda_send_verbs(audioGroup->codec, &verb, NULL, 1) != B_OK) { + dprintf("hda: Setting output selector %ld failed on widget %ld!\n", + widget.active_input, widget.node_id); + } + } + + return B_OK; } @@ -596,135 +679,20 @@ hda_codec_new_audio_group(hda_codec* codec, uint32 audioGroupNodeID) /* Setup for worst-case scenario; we cannot find any output Pin Widgets */ status = ENODEV; - /* Try to locate all input/output channels */ - for (uint32 i = 0; i < audioGroup->widget_count; i++) { - uint32 outputWidget = 0, inputWidget = 0; + if (hda_audio_group_build_tree(audioGroup) != B_OK) + goto err; - if (audioGroup->playback_stream == NULL - && audioGroup->widgets[i].type == WT_PIN_COMPLEX - && audioGroup->widgets[i].d.pin.output) { - if (audioGroup->widgets[i].d.pin.device == PIN_DEV_HEAD_PHONE_OUT - || audioGroup->widgets[i].d.pin.device == PIN_DEV_SPEAKER - || audioGroup->widgets[i].d.pin.device == PIN_DEV_LINE_OUT) { - int32 inputIndex = audioGroup->widgets[i].active_input; - if (inputIndex != -1) { - outputWidget = hda_codec_audio_group_find_path(audioGroup, - audioGroup->widgets[i].inputs[inputIndex], - WT_AUDIO_OUTPUT, 0); - } else { - // find and select output widget - for (inputIndex = 0; (uint32)inputIndex - < audioGroup->widgets[i].num_inputs; inputIndex++) { - outputWidget = hda_codec_audio_group_find_path(audioGroup, - audioGroup->widgets[i].inputs[inputIndex], - WT_AUDIO_OUTPUT, 0); - if (outputWidget) { - corb_t verb = MAKE_VERB(codec->addr, - i + audioGroup->widget_start, - VID_SET_CONNECTION_SELECT, inputIndex); - if (hda_send_verbs(codec, &verb, NULL, 1) != B_OK) - dprintf("%s: Setting output selector failed!\n", __func__); - break; - } - } - } + audioGroup->playback_stream = hda_stream_new(audioGroup, STREAM_PLAYBACK); + //audioGroup->record_stream = hda_stream_new(audioGroup, STREAM_RECORD); - if (outputWidget) { - if (!audioGroup->playback_stream) { - corb_t verb[2]; - - /* Setup playback/record streams for Multi Audio API */ - audioGroup->playback_stream = hda_stream_new( - audioGroup->codec->controller, STREAM_PLAYBACK); - audioGroup->record_stream = hda_stream_new( - audioGroup->codec->controller, STREAM_RECORD); - - audioGroup->playback_stream->pin_widget = i - + audioGroup->widget_start; - audioGroup->playback_stream->io_widget = outputWidget; - - /* FIXME: Force Pin Widget to unmute; enable hp/output */ - verb[0] = MAKE_VERB(codec->addr, - audioGroup->playback_stream->pin_widget, - VID_SET_AMPLIFIER_GAIN_MUTE, - AMP_SET_OUTPUT | AMP_SET_LEFT_CHANNEL - | AMP_SET_RIGHT_CHANNEL); - verb[1] = MAKE_VERB(codec->addr, - audioGroup->playback_stream->pin_widget, - VID_SET_PIN_WIDGET_CONTROL, - PIN_ENABLE_HEAD_PHONE | PIN_ENABLE_OUTPUT); - hda_send_verbs(codec, verb, NULL, 2); - - dprintf("%s: Found output PIN (%s) connected to output " - "CONV wid:%ld\n", __func__, - kDefaultDevice[audioGroup->widgets[i].d.pin.device], outputWidget); - } - } - } - } - - if (audioGroup->widgets[i].type == WT_AUDIO_INPUT) { - int32 inputIndex = audioGroup->widgets[i].active_input; - if (inputIndex != -1) { - inputWidget = hda_codec_audio_group_find_path(audioGroup, - audioGroup->widgets[i].inputs[inputIndex], WT_PIN_COMPLEX, - 0); - } else { - // find and select input widget - for (inputIndex = 0; (uint32)inputIndex - < audioGroup->widgets[i].num_inputs; inputIndex++) { - inputWidget = hda_codec_audio_group_find_path(audioGroup, - audioGroup->widgets[i].inputs[inputIndex], - WT_PIN_COMPLEX, 0); - if (inputWidget) { - corb_t verb = MAKE_VERB(codec->addr, - i + audioGroup->widget_start, - VID_SET_CONNECTION_SELECT, inputIndex); - if (hda_send_verbs(codec, &verb, NULL, 1) != B_OK) { - dprintf("%s: Setting input selector failed!\n", - __func__); - } - break; - } - } - } - - if (inputWidget) { - if (!audioGroup->record_stream) { - corb_t verb; - - /* Setup playback/record streams for Multi Audio API */ - audioGroup->record_stream = hda_stream_new( - audioGroup->codec->controller, STREAM_RECORD); - - audioGroup->record_stream->pin_widget = inputWidget; - audioGroup->record_stream->io_widget = i - + audioGroup->widget_start; - - /* FIXME: Force Pin Widget to unmute */ - verb = MAKE_VERB(codec->addr, - audioGroup->record_stream->pin_widget, - VID_SET_AMPLIFIER_GAIN_MUTE, - AMP_SET_INPUT | AMP_SET_LEFT_CHANNEL - | AMP_SET_RIGHT_CHANNEL); - hda_send_verbs(codec, &verb, NULL, 1); - } - - dprintf("%s: Found input PIN (%s) connected to input CONV " - "wid:%ld\n", __func__, kDefaultDevice[audioGroup->widgets[ - inputWidget - audioGroup->widget_start].d.pin.device], - i + audioGroup->widget_start); - } - } - } - - /* If we found any valid output channels, we're in the clear */ - if (audioGroup && audioGroup->playback_stream) { + if (audioGroup->playback_stream != NULL + || audioGroup->record_stream != NULL) { codec->audio_groups[codec->num_audio_groups++] = audioGroup; return B_OK; } err: + free(audioGroup->widgets); free(audioGroup); return status; } @@ -733,6 +701,79 @@ err: // #pragma mark - +status_t +hda_audio_group_get_widgets(hda_audio_group* audioGroup, hda_stream* stream) +{ + hda_widget_type type; + uint32 flags; + + if (stream->type == STREAM_PLAYBACK) { + type = WT_AUDIO_OUTPUT; + flags = WIDGET_FLAG_OUTPUT_PATH; + } else { + // record + type = WT_AUDIO_INPUT; + flags = WIDGET_FLAG_INPUT_PATH; + } + + uint32 count = 0; + + for (uint32 i = 0; i < audioGroup->widget_count && count < MAX_IO_WIDGETS; + i++) { + hda_widget& widget = audioGroup->widgets[i]; + + if ((widget.flags & flags) != 0) { + if (widget.type == WT_PIN_COMPLEX) { + stream->pin_widget = widget.node_id; + +dprintf("ENABLE pin widget %ld\n", widget.node_id); + /* FIXME: Force Pin Widget to unmute; enable hp/output */ + uint32 verb = MAKE_VERB(audioGroup->codec->addr, + widget.node_id, + VID_SET_PIN_WIDGET_CONTROL, + PIN_ENABLE_HEAD_PHONE | PIN_ENABLE_OUTPUT); + hda_send_verbs(audioGroup->codec, &verb, NULL, 1); + } + + if (widget.capabilities.output_amplifier != 0) { +dprintf("UNMUTE/SET GAIN widget %ld (offset %ld)\n", widget.node_id, + widget.capabilities.output_amplifier & AMP_CAP_OFFSET_MASK); + uint32 verb = MAKE_VERB(audioGroup->codec->addr, + widget.node_id, + VID_SET_AMPLIFIER_GAIN_MUTE, + AMP_SET_OUTPUT | AMP_SET_LEFT_CHANNEL + | AMP_SET_RIGHT_CHANNEL + | (widget.capabilities.output_amplifier + & AMP_CAP_OFFSET_MASK)); + hda_send_verbs(audioGroup->codec, &verb, NULL, 1); + } + } + + if (widget.type != type || (widget.flags & flags) == 0 + || (widget.capabilities.audio + & (AUDIO_CAP_STEREO | AUDIO_CAP_DIGITAL)) != AUDIO_CAP_STEREO + || widget.d.io.formats == 0) + continue; + + if (count == 0) { + stream->sample_format = widget.d.io.formats; + stream->sample_rate = widget.d.io.rates; + } else { + stream->sample_format &= widget.d.io.formats; + stream->sample_rate &= widget.d.io.rates; + } + + stream->io_widgets[count++] = widget.node_id; + } + + if (count == 0) + return B_ENTRY_NOT_FOUND; + + stream->num_io_widgets = count; + return B_OK; +} + + void hda_codec_delete(hda_codec* codec) { @@ -783,7 +824,7 @@ hda_codec_new(hda_controller* controller, uint32 codecAddress) verbs[0] = MAKE_VERB(codecAddress, 0, VID_GET_PARAMETER, PID_VENDOR_ID); verbs[1] = MAKE_VERB(codecAddress, 0, VID_GET_PARAMETER, PID_REVISION_ID); verbs[2] = MAKE_VERB(codecAddress, 0, VID_GET_PARAMETER, - PID_SUBORDINATE_NODE_COUNT); + PID_SUB_NODE_COUNT); if (hda_send_verbs(codec, verbs, (uint32*)&response, 3) != B_OK) goto err; diff --git a/src/add-ons/kernel/drivers/audio/hda/hda_codec_defs.h b/src/add-ons/kernel/drivers/audio/hda/hda_codec_defs.h index 048e145280..69e6e34601 100644 --- a/src/add-ons/kernel/drivers/audio/hda/hda_codec_defs.h +++ b/src/add-ons/kernel/drivers/audio/hda/hda_codec_defs.h @@ -121,7 +121,7 @@ enum pin_dev_type { /* Parameter IDs */ #define PID_VENDOR_ID 0x00 #define PID_REVISION_ID 0x02 -#define PID_SUBORDINATE_NODE_COUNT 0x04 +#define PID_SUB_NODE_COUNT 0x04 #define PID_FUNCTION_GROUP_TYPE 0x05 #define PID_AUDIO_GROUP_CAP 0x08 #define PID_AUDIO_WIDGET_CAP 0x09 diff --git a/src/add-ons/kernel/drivers/audio/hda/hda_controller.cpp b/src/add-ons/kernel/drivers/audio/hda/hda_controller.cpp index 764d3bf424..75444932c1 100644 --- a/src/add-ons/kernel/drivers/audio/hda/hda_controller.cpp +++ b/src/add-ons/kernel/drivers/audio/hda/hda_controller.cpp @@ -325,8 +325,10 @@ hda_stream_delete(hda_stream* stream) hda_stream* -hda_stream_new(hda_controller* controller, int type) +hda_stream_new(hda_audio_group* audioGroup, int type) { + hda_controller* controller = audioGroup->codec->controller; + hda_stream* stream = (hda_stream*)calloc(1, sizeof(hda_stream)); if (stream == NULL) return NULL; @@ -339,7 +341,7 @@ hda_stream_new(hda_controller* controller, int type) switch (type) { case STREAM_PLAYBACK: stream->id = 1; - stream->off = (controller->num_input_streams * HDAC_SDSIZE); + stream->off = controller->num_input_streams * HDAC_SDSIZE; controller->streams[controller->num_input_streams] = stream; break; @@ -356,7 +358,13 @@ hda_stream_new(hda_controller* controller, int type) break; } - return stream; + // find I/O and Pin widgets for this stream + + if (hda_audio_group_get_widgets(audioGroup, stream) == B_OK) + return stream; + + free(stream); + return NULL; } @@ -543,11 +551,16 @@ dprintf("HDA: sample size %ld, num channels %ld, buffer length %ld ************* REG32(audioGroup->codec->controller, DMA_POSITION_BASE_LOWER) |= DMA_POSITION_ENABLED; - verb[0] = MAKE_VERB(audioGroup->codec->addr, stream->io_widget, - VID_SET_CONVERTER_FORMAT, format); - verb[1] = MAKE_VERB(audioGroup->codec->addr, stream->io_widget, - VID_SET_CONVERTER_STREAM_CHANNEL, stream->id << 4); - return hda_send_verbs(audioGroup->codec, verb, response, 2); + for (uint32 i = 0; i < stream->num_io_widgets; i++) { + verb[0] = MAKE_VERB(audioGroup->codec->addr, stream->io_widgets[i], + VID_SET_CONVERTER_FORMAT, format); + verb[1] = MAKE_VERB(audioGroup->codec->addr, stream->io_widgets[i], + VID_SET_CONVERTER_STREAM_CHANNEL, stream->id << 4); + hda_send_verbs(audioGroup->codec, verb, response, 2); + } + + snooze(1000); + return B_OK; } diff --git a/src/add-ons/kernel/drivers/audio/hda/hda_multi_audio.cpp b/src/add-ons/kernel/drivers/audio/hda/hda_multi_audio.cpp index 942451030b..6aa9e31fad 100644 --- a/src/add-ons/kernel/drivers/audio/hda/hda_multi_audio.cpp +++ b/src/add-ons/kernel/drivers/audio/hda/hda_multi_audio.cpp @@ -4,6 +4,7 @@ * * Authors: * Ithamar Adema, ithamar AT unet DOT nl + * Axel Dörfler, axeld@pinc-software.de */ @@ -67,10 +68,18 @@ get_description(hda_audio_group* audioGroup, multi_description* data) strcpy(data->friendly_name, "HD Audio"); strcpy(data->vendor_info, "Haiku"); - data->output_channel_count = 2; - data->input_channel_count = 2; - data->output_bus_channel_count = 2; - data->input_bus_channel_count = 2; + int32 inChannels = 0; + if (audioGroup->record_stream != NULL) + inChannels = 2; + + int32 outChannels = 0; + if (audioGroup->playback_stream != NULL) + outChannels = 2; + + data->output_channel_count = outChannels; + data->output_bus_channel_count = outChannels; + data->input_channel_count = inChannels; + data->input_bus_channel_count = inChannels; data->aux_bus_channel_count = 0; dprintf("%s: request_channel_count: %ld\n", __func__, @@ -81,7 +90,7 @@ get_description(hda_audio_group* audioGroup, multi_description* data) memcpy(data->channels, &sChannels, sizeof(sChannels)); } - /* determine output/input rates */ + /* determine output/input rates */ data->output_rates = audioGroup->supported_rates; data->input_rates = audioGroup->supported_rates; @@ -127,11 +136,21 @@ get_global_format(hda_audio_group* audioGroup, multi_format_info* data) data->input_latency = 0; data->timecode_kind = 0; - data->output.format = audioGroup->playback_stream->sample_format; - data->output.rate = audioGroup->playback_stream->sample_rate; + if (audioGroup->playback_stream != NULL) { + data->output.format = audioGroup->playback_stream->sample_format; + data->output.rate = audioGroup->playback_stream->sample_rate; + } else { + data->output.format = 0; + data->output.rate = 0; + } - data->input.format = audioGroup->record_stream->sample_format; - data->input.rate = audioGroup->record_stream->sample_format; + if (audioGroup->record_stream != NULL) { + data->input.format = audioGroup->record_stream->sample_format; + data->input.rate = audioGroup->record_stream->sample_format; + } else { + data->input.format = 0; + data->input.rate = 0; + } return B_OK; } @@ -147,15 +166,19 @@ set_global_format(hda_audio_group* audioGroup, multi_format_info* data) return B_BAD_VALUE; #endif - audioGroup->playback_stream->sample_format = data->output.format; - audioGroup->playback_stream->sample_rate = data->output.rate; - audioGroup->playback_stream->sample_size = format2size( - audioGroup->playback_stream->sample_format); + if (audioGroup->playback_stream != NULL) { + audioGroup->playback_stream->sample_format = data->output.format; + audioGroup->playback_stream->sample_rate = data->output.rate; + audioGroup->playback_stream->sample_size = format2size( + audioGroup->playback_stream->sample_format); + } - audioGroup->record_stream->sample_rate = data->input.rate; - audioGroup->record_stream->sample_format = data->input.format; - audioGroup->record_stream->sample_size = format2size( - audioGroup->record_stream->sample_format); + if (audioGroup->record_stream != NULL) { + audioGroup->record_stream->sample_rate = data->input.rate; + audioGroup->record_stream->sample_format = data->input.format; + audioGroup->record_stream->sample_size = format2size( + audioGroup->record_stream->sample_format); + } return B_OK; } @@ -227,56 +250,66 @@ get_buffers(hda_audio_group* audioGroup, multi_buffer_list* data) data->flags = B_MULTI_BUFFER_PLAYBACK; /* Copy the settings into the streams */ - audioGroup->playback_stream->num_buffers = data->return_playback_buffers; - audioGroup->playback_stream->num_channels = data->return_playback_channels; - audioGroup->playback_stream->buffer_length - = data->return_playback_buffer_size; - status_t status = hda_stream_setup_buffers(audioGroup, - audioGroup->playback_stream, "Playback"); - if (status != B_OK) { - dprintf("hda: Error setting up playback buffers: %s\n", - strerror(status)); - return status; + if (audioGroup->playback_stream != NULL) { + audioGroup->playback_stream->num_buffers = data->return_playback_buffers; + audioGroup->playback_stream->num_channels = data->return_playback_channels; + audioGroup->playback_stream->buffer_length + = data->return_playback_buffer_size; + + status_t status = hda_stream_setup_buffers(audioGroup, + audioGroup->playback_stream, "Playback"); + if (status != B_OK) { + dprintf("hda: Error setting up playback buffers: %s\n", + strerror(status)); + return status; + } } - audioGroup->record_stream->num_buffers = data->return_record_buffers; - audioGroup->record_stream->num_channels = data->return_record_channels; - audioGroup->record_stream->buffer_length - = data->return_record_buffer_size; + if (audioGroup->record_stream != NULL) { + audioGroup->record_stream->num_buffers = data->return_record_buffers; + audioGroup->record_stream->num_channels = data->return_record_channels; + audioGroup->record_stream->buffer_length + = data->return_record_buffer_size; - status = hda_stream_setup_buffers(audioGroup, audioGroup->record_stream, - "Recording"); - if (status != B_OK) { - dprintf("hda: Error setting up recording buffers: %s\n", - strerror(status)); - return status; + status_t status = hda_stream_setup_buffers(audioGroup, + audioGroup->record_stream, "Recording"); + if (status != B_OK) { + dprintf("hda: Error setting up recording buffers: %s\n", + strerror(status)); + return status; + } } /* Setup data structure for multi_audio API... */ - uint32 playbackSampleSize = audioGroup->playback_stream->sample_size; - uint32 recordSampleSize = audioGroup->record_stream->sample_size; + if (audioGroup->playback_stream != NULL) { + uint32 playbackSampleSize = audioGroup->playback_stream->sample_size; - for (int32 i = 0; i < data->return_playback_buffers; i++) { - for (int32 channelIndex = 0; - channelIndex < data->return_playback_channels; channelIndex++) { - data->playback_buffers[i][channelIndex].base - = (char*)audioGroup->playback_stream->buffers[i] - + playbackSampleSize * channelIndex; - data->playback_buffers[i][channelIndex].stride - = playbackSampleSize * data->return_playback_channels; + for (int32 i = 0; i < data->return_playback_buffers; i++) { + for (int32 channelIndex = 0; + channelIndex < data->return_playback_channels; channelIndex++) { + data->playback_buffers[i][channelIndex].base + = (char*)audioGroup->playback_stream->buffers[i] + + playbackSampleSize * channelIndex; + data->playback_buffers[i][channelIndex].stride + = playbackSampleSize * data->return_playback_channels; + } } } - for (int32 i = 0; i < data->return_record_buffers; i++) { - for (int32 channelIndex = 0; - channelIndex < data->return_record_channels; channelIndex++) { - data->record_buffers[i][channelIndex].base - = (char*)audioGroup->record_stream->buffers[i] - + recordSampleSize * channelIndex; - data->record_buffers[i][channelIndex].stride - = recordSampleSize * data->return_record_channels; + if (audioGroup->record_stream != NULL) { + uint32 recordSampleSize = audioGroup->record_stream->sample_size; + + for (int32 i = 0; i < data->return_record_buffers; i++) { + for (int32 channelIndex = 0; + channelIndex < data->return_record_channels; channelIndex++) { + data->record_buffers[i][channelIndex].base + = (char*)audioGroup->record_stream->buffers[i] + + recordSampleSize * channelIndex; + data->record_buffers[i][channelIndex].stride + = recordSampleSize * data->return_record_channels; + } } } @@ -292,6 +325,10 @@ buffer_exchange(hda_audio_group* audioGroup, multi_buffer_info* data) cpu_status status; status_t rc; + // TODO: support recording! + if (audioGroup->playback_stream == NULL) + return B_ERROR; + if (!audioGroup->playback_stream->running) { hda_stream_start(audioGroup->codec->controller, audioGroup->playback_stream); @@ -328,7 +365,10 @@ buffer_exchange(hda_audio_group* audioGroup, multi_buffer_info* data) static status_t buffer_force_stop(hda_audio_group* audioGroup) { - hda_stream_stop(audioGroup->codec->controller, audioGroup->playback_stream); + if (audioGroup->playback_stream != NULL) { + hda_stream_stop(audioGroup->codec->controller, + audioGroup->playback_stream); + } //hda_stream_stop(audioGroup->codec->controller, audioGroup->record_stream); return B_OK; @@ -351,7 +391,34 @@ multi_audio_control(void* cookie, uint32 op, void* arg, size_t len) switch (op) { case B_MULTI_GET_DESCRIPTION: + { +#ifdef __HAIKU + multi_description description; + multi_channel_info channels[16]; + multi_channel_info* originalChannels; + + if (user_memcpy(&description, arg, sizeof(multi_description)) + != B_OK) + return B_BAD_ADDRESS; + + originalChannels = description.channels; + description.channels = channels; + if (description.request_channel_count > 16) + description.request_channel_count = 16; + + status_t status = get_description(audioGroup, &description); + if (status != B_OK) + return status; + + description.channels = originalChannels; + return user_memcpy(arg, &description, sizeof(multi_description)) + && user_memcpy(originalChannels, channels, + sizeof(multi_channel_info) + * description.request_channel_count; +#else return get_description(audioGroup, (multi_description*)arg); +#endif + } case B_MULTI_GET_ENABLED_CHANNELS: return get_enabled_channels(audioGroup, (multi_channel_enable*)arg);