* 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:
parent
4ca14fe1a6
commit
30f55bc990
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user