* 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
This commit is contained in:
Axel Dörfler 2008-03-13 12:25:29 +00:00
parent 4ca14fe1a6
commit 30f55bc990
5 changed files with 356 additions and 232 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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);