From 8c3da8dd2ca2427d7efa609ef8708bdf7d07da92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Sun, 16 Nov 2008 23:41:14 +0000 Subject: [PATCH] added a mixer interface git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@28677 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/kernel/drivers/audio/hda/driver.h | 54 +- .../kernel/drivers/audio/hda/hda_codec.cpp | 116 ++++- .../kernel/drivers/audio/hda/hda_codec_defs.h | 51 +- .../drivers/audio/hda/hda_controller.cpp | 17 +- .../drivers/audio/hda/hda_multi_audio.cpp | 471 +++++++++++++++++- 5 files changed, 666 insertions(+), 43 deletions(-) diff --git a/src/add-ons/kernel/drivers/audio/hda/driver.h b/src/add-ons/kernel/drivers/audio/hda/driver.h index 4d4544377e..4e5287a1fb 100644 --- a/src/add-ons/kernel/drivers/audio/hda/driver.h +++ b/src/add-ons/kernel/drivers/audio/hda/driver.h @@ -40,8 +40,10 @@ #define MAX_CODEC_RESPONSES 10 #define MAX_INPUTS 32 #define MAX_IO_WIDGETS 8 +#define MAX_ASSOCIATIONS 16 +#define MAX_ASSOCIATION_PINS 16 -#define DEFAULT_FRAMES_PER_BUFFER 512 +#define DEFAULT_FRAMES_PER_BUFFER 2048 #define STREAM_MAX_BUFFERS 10 #define STREAM_MIN_BUFFERS 2 @@ -53,6 +55,7 @@ enum { struct hda_codec; struct hda_stream; +struct hda_multi; /*! This structure describes a single HDA compliant controller. It contains a list of available streams @@ -215,13 +218,21 @@ struct hda_widget { struct { uint32 output; uint32 input; - pin_dev_type device; + uint32 config; } pin; } d; }; +struct hda_association { + uint32 index; + bool enabled; + uint32 pin_count; + uint32 pins[MAX_ASSOCIATION_PINS]; +}; + #define WIDGET_FLAG_OUTPUT_PATH 0x01 #define WIDGET_FLAG_INPUT_PATH 0x02 +#define WIDGET_FLAG_WIDGET_PATH 0x04 /*! This structure describes a single Audio Function Group. An AFG is a group of audio widgets which can be used to configure multiple @@ -246,7 +257,12 @@ struct hda_audio_group { uint32 input_amplifier_capabilities; uint32 output_amplifier_capabilities; + uint32 association_count; + hda_widget* widgets; + hda_association associations[MAX_ASSOCIATIONS]; + + hda_multi* multi; }; /*! This structure describes a single codec module in the @@ -274,6 +290,38 @@ struct hda_codec { }; +#define MULTI_CONTROL_FIRSTID 1024 +#define MULTI_CONTROL_MASTERID 0 +#define MULTI_MAX_CONTROLS 128 +#define MULTI_MAX_CHANNELS 128 + +struct hda_multi_mixer_control { + hda_multi *multi; + int32 nid; + int32 type; + bool input; + uint32 mute; + uint32 gain; + uint32 capabilities; + int32 index; + multi_mix_control mix_control; +}; + + +struct hda_multi { + hda_audio_group *group; + hda_multi_mixer_control controls[MULTI_MAX_CONTROLS]; + uint32 control_count; + + multi_channel_info chans[MULTI_MAX_CHANNELS]; + uint32 output_channel_count; + uint32 input_channel_count; + uint32 output_bus_channel_count; + uint32 input_bus_channel_count; + uint32 aux_bus_channel_count; +}; + + /* driver.c */ extern device_hooks gDriverHooks; extern pci_module_info* gPci; @@ -281,6 +329,8 @@ extern hda_controller gCards[MAX_CARDS]; extern uint32 gNumCards; /* hda_codec.c */ +hda_widget* hda_audio_group_get_widget(hda_audio_group* audioGroup, uint32 nodeID); + status_t hda_audio_group_get_widgets(hda_audio_group* audioGroup, hda_stream* stream); hda_codec* hda_codec_new(hda_controller* controller, uint32 cad); 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 0160a71682..ba67b70271 100644 --- a/src/add-ons/kernel/drivers/audio/hda/hda_codec.cpp +++ b/src/add-ons/kernel/drivers/audio/hda/hda_codec.cpp @@ -65,7 +65,7 @@ get_widget_type_name(hda_widget_type type) static void dump_widget_audio_capabilities(uint32 capabilities) { - const struct { + static const struct { uint32 flag; const char* name; } kFlags[] = { @@ -114,7 +114,7 @@ dump_widget_inputs(hda_widget& widget) } if (offset != 0) - dprintf("\tConnections: %s\n", buffer); + dprintf("\tInputs: %s\n", buffer); } @@ -264,8 +264,9 @@ hda_get_stream_support(hda_codec* codec, uint32 nodeID, uint32* formats, } if ((resp[0] & STREAM_FLOAT) != 0) *formats |= B_FMT_FLOAT; - -//FIXME: if (resp[0] & (1 << 2)) /* Sort out how to handle AC3 */; + if ((resp[0] & STREAM_AC3) != 0) { + *formats |= B_FMT_BITSTREAM; + } return B_OK; } @@ -342,7 +343,7 @@ hda_widget_get_amplifier_capabilities(hda_audio_group* audioGroup, } -static hda_widget* +hda_widget* hda_audio_group_get_widget(hda_audio_group* audioGroup, uint32 nodeID) { if (audioGroup->widget_start > nodeID @@ -434,6 +435,40 @@ hda_widget_get_connections(hda_audio_group* audioGroup, hda_widget* widget) } +static status_t +hda_widget_get_associations(hda_audio_group* audioGroup) +{ + uint32 index = 0; + for (uint32 i = 0; i < MAX_ASSOCIATIONS; i++) { + for (uint32 j = 0; j < audioGroup->widget_count; j++) { + hda_widget& widget = audioGroup->widgets[j]; + + if (widget.type != WT_PIN_COMPLEX) + continue; + if (CONF_DEFAULT_ASSOCIATION(widget.d.pin.config) != i) + continue; + if (audioGroup->associations[index].pin_count == 0) { + audioGroup->associations[index].index = index; + audioGroup->associations[index].enabled = true; + } + uint32 sequence = CONF_DEFAULT_SEQUENCE(widget.d.pin.config); + if (audioGroup->associations[index].pins[sequence] != 0) { + audioGroup->associations[index].enabled = false; + } + audioGroup->associations[index].pins[sequence] = widget.node_id; + audioGroup->associations[index].pin_count++; + if (i == 15) + index++; + } + if (i != 15 && audioGroup->associations[index].pin_count != 0) + index++; + } + audioGroup->association_count = index; + + return B_OK; +} + + // #pragma mark - audio group functions @@ -534,13 +569,13 @@ hda_codec_parse_audio_group(hda_audio_group* audioGroup) verbs[0] = MAKE_VERB(audioGroup->codec->addr, nodeID, VID_GET_CONFIGURATION_DEFAULT, 0); if (hda_send_verbs(audioGroup->codec, verbs, resp, 1) == B_OK) { - widget.d.pin.device = (pin_dev_type) - ((resp[0] >> 20) & 0xf); - dprintf("\t%s, %s, %s, %s\n", - kPortConnector[resp[0] >> 30], - kDefaultDevice[widget.d.pin.device], - kConnectionType[(resp[0] >> 16) & 0xF], - kJackColor[(resp[0] >> 12) & 0xF]); + widget.d.pin.config = resp[0]; + dprintf("\t%s, %s, %s, %s, Association:%ld\n", + kPortConnector[CONF_DEFAULT_CONNECTIVITY(resp[0])], + kDefaultDevice[CONF_DEFAULT_DEVICE(resp[0])], + kConnectionType[CONF_DEFAULT_CONNTYPE(resp[0])], + kJackColor[CONF_DEFAULT_COLOR(resp[0])], + CONF_DEFAULT_ASSOCIATION(resp[0])); } break; @@ -558,6 +593,8 @@ hda_codec_parse_audio_group(hda_audio_group* audioGroup) dump_widget_inputs(widget); } + hda_widget_get_associations(audioGroup); + return B_OK; } @@ -619,13 +656,20 @@ hda_widget_find_input_path(hda_audio_group* audioGroup, hda_widget* widget, switch (widget->type) { case WT_PIN_COMPLEX: - if (widget->d.pin.input - && (widget->d.pin.device == PIN_DEV_CD - || widget->d.pin.device == PIN_DEV_LINE_IN - || widget->d.pin.device == PIN_DEV_MIC_IN)) { - widget->flags |= WIDGET_FLAG_INPUT_PATH; + // already used + if (widget->flags & WIDGET_FLAG_INPUT_PATH) + return false; + + if (widget->d.pin.input) { + switch (CONF_DEFAULT_DEVICE(widget->d.pin.config)) { + case PIN_DEV_CD: + case PIN_DEV_LINE_IN: + case PIN_DEV_MIC_IN: + widget->flags |= WIDGET_FLAG_INPUT_PATH; dprintf(" %*sinput: added input widget %ld\n", (int)depth * 2, "", widget->node_id); - return true; + return true; + break; + } } return false; case WT_AUDIO_INPUT: @@ -670,10 +714,13 @@ 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)) + if (widget.type != WT_PIN_COMPLEX || !widget.d.pin.output) + continue; + + int device = CONF_DEFAULT_DEVICE(widget.d.pin.config); + if (device != PIN_DEV_HEAD_PHONE_OUT + && device != PIN_DEV_SPEAKER + && device != PIN_DEV_LINE_OUT) continue; dprintf(" look at pin widget %ld (%ld inputs)\n", widget.node_id, widget.num_inputs); @@ -792,7 +839,7 @@ hda_codec_delete_audio_group(hda_audio_group* audioGroup) if (audioGroup->record_stream != NULL) hda_stream_delete(audioGroup->record_stream); - + free(audioGroup->multi); free(audioGroup->widgets); free(audioGroup); } @@ -809,6 +856,11 @@ hda_codec_new_audio_group(hda_codec* codec, uint32 audioGroupNodeID) /* Setup minimal info needed by hda_codec_parse_afg */ audioGroup->root_node_id = audioGroupNodeID; audioGroup->codec = codec; + audioGroup->multi = (hda_multi*)calloc(1, + sizeof(hda_multi)); + if (audioGroup->multi == NULL) + return B_NO_MEMORY; + audioGroup->multi->group = audioGroup; /* Parse all widgets in Audio Function Group */ status_t status = hda_codec_parse_audio_group(audioGroup); @@ -878,8 +930,9 @@ dprintf("ENABLE pin widget %ld\n", widget.node_id); 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, + if (widget.capabilities.output_amplifier != 0 + && widget.flags & WIDGET_FLAG_OUTPUT_PATH) { +dprintf("UNMUTE/SET OUTPUT 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, @@ -890,6 +943,19 @@ dprintf("UNMUTE/SET GAIN widget %ld (offset %ld)\n", widget.node_id, & AMP_CAP_OFFSET_MASK)); hda_send_verbs(audioGroup->codec, &verb, NULL, 1); } + if (widget.capabilities.input_amplifier != 0 + && widget.flags & WIDGET_FLAG_INPUT_PATH) { +dprintf("UNMUTE/SET INPUT 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_INPUT | AMP_SET_LEFT_CHANNEL + | AMP_SET_RIGHT_CHANNEL + | (widget.capabilities.input_amplifier + & AMP_CAP_OFFSET_MASK)); + hda_send_verbs(audioGroup->codec, &verb, NULL, 1); + } } if (widget.type != type || (widget.flags & flags) == 0 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 78b14b3458..5c23ebbe5a 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 @@ -21,6 +21,12 @@ enum hda_widget_type { WT_VENDOR_DEFINED = 15 }; +enum pin_connectivity_type { + PIN_CONN_JACK, + PIN_CONN_NONE, + PIN_CONN_FIXED, + PIN_CONN_BOTH +}; enum pin_dev_type { PIN_DEV_LINE_OUT = 0, @@ -163,6 +169,12 @@ enum pin_dev_type { #define AMP_CAP_NUM_STEPS_SHIFT 8 #define AMP_CAP_OFFSET_MASK 0x0000007f +#define AMP_CAP_STEP_SIZE(c) ((((c & AMP_CAP_STEP_SIZE_MASK) \ + >> AMP_CAP_STEP_SIZE_SHIFT) + 1) / 4.0) +#define AMP_CAP_NUM_STEPS(c) ((c & AMP_CAP_NUM_STEPS_MASK) \ + >> AMP_CAP_NUM_STEPS_SHIFT) +#define AMP_CAP_OFFSET(c) (c & AMP_CAP_OFFSET_MASK) + /* Pin capabilities */ #define PIN_CAP_IMP_SENSE (1L << 0) #define PIN_CAP_TRIGGER_REQ (1L << 1) @@ -171,6 +183,12 @@ enum pin_dev_type { #define PIN_CAP_OUT (1L << 4) #define PIN_CAP_IN (1L << 5) #define PIN_CAP_BALANCE (1L << 6) +#define PIN_CAP_VREF_CTRL_HIZ (1L << 8) +#define PIN_CAP_VREF_CTRL_50 (1L << 9) +#define PIN_CAP_VREF_CTRL_GROUND (1L << 10) +#define PIN_CAP_VREF_CTRL_80 (1L << 12) +#define PIN_CAP_VREF_CTRL_100 (1L << 13) +#define PIN_CAP_EAPD_CAP (1L << 16) /* PCM support */ #define PCM_8_BIT (1L << 16) @@ -192,6 +210,8 @@ enum pin_dev_type { #define AMP_GET_INPUT_INDEX_MASK 0x0000000f #define AMP_GET_INPUT_INDEX_SHIFT 0 +#define AMP_GET_INPUT_INDEX(x) ((x << AMP_GET_INPUT_INDEX_SHIFT) & AMP_GET_INPUT_INDEX_MASK) + #define AMP_SET_OUTPUT (1L << 15) #define AMP_SET_INPUT (1L << 14) #define AMP_SET_LEFT_CHANNEL (1L << 13) @@ -199,8 +219,10 @@ enum pin_dev_type { #define AMP_SET_INPUT_INDEX_MASK 0x00000f00 #define AMP_SET_INPUT_INDEX_SHIFT 8 +#define AMP_SET_INPUT_INDEX(x) ((x << AMP_SET_INPUT_INDEX_SHIFT) & AMP_SET_INPUT_INDEX_MASK) + #define AMP_GAIN_MASK 0x0000007f -#define AMP_MUTE (1L << 8) +#define AMP_MUTE (1L << 7) /* Pin Widget Control */ #define PIN_ENABLE_HEAD_PHONE (1L << 7) @@ -214,4 +236,31 @@ enum pin_dev_type { #define POWER_STATE_D2 (1L << 2) #define POWER_STATE_D3 (1L << 3) +/* Configuration default */ +#define CONF_DEFAULT_SEQUENCE_MASK 0x0000000f +#define CONF_DEFAULT_SEQUENCE_SHIFT 0 +#define CONF_DEFAULT_ASSOCIATION_MASK 0x000000f0 +#define CONF_DEFAULT_ASSOCIATION_SHIFT 4 +#define CONF_DEFAULT_MISC_MASK 0x00000f00 +#define CONF_DEFAULT_MISC_SHIFT 8 +#define CONF_DEFAULT_COLOR_MASK 0x0000f000 +#define CONF_DEFAULT_COLOR_SHIFT 12 +#define CONF_DEFAULT_CONNTYPE_MASK 0x000f0000 +#define CONF_DEFAULT_CONNTYPE_SHIFT 16 +#define CONF_DEFAULT_DEVICE_MASK 0x00f00000 +#define CONF_DEFAULT_DEVICE_SHIFT 20 +#define CONF_DEFAULT_LOCATION_MASK 0x3f000000 +#define CONF_DEFAULT_LOCATION_SHIFT 24 +#define CONF_DEFAULT_CONNECTIVITY_MASK 0xc0000000 +#define CONF_DEFAULT_CONNECTIVITY_SHIFT 30 + +#define CONF_DEFAULT_SEQUENCE(c) ((c & CONF_DEFAULT_SEQUENCE_MASK) >> CONF_DEFAULT_SEQUENCE_SHIFT) +#define CONF_DEFAULT_ASSOCIATION(c) ((c & CONF_DEFAULT_ASSOCIATION_MASK) >> CONF_DEFAULT_ASSOCIATION_SHIFT) +#define CONF_DEFAULT_MISC(c) ((c & CONF_DEFAULT_MISC_MASK) >> CONF_DEFAULT_MISC_SHIFT) +#define CONF_DEFAULT_COLOR(c) ((c & CONF_DEFAULT_COLOR_MASK) >> CONF_DEFAULT_COLOR_SHIFT) +#define CONF_DEFAULT_CONNTYPE(c) ((c & CONF_DEFAULT_CONNTYPE_MASK) >> CONF_DEFAULT_CONNTYPE_SHIFT) +#define CONF_DEFAULT_DEVICE(c) ((c & CONF_DEFAULT_DEVICE_MASK) >> CONF_DEFAULT_DEVICE_SHIFT) +#define CONF_DEFAULT_LOCATION(c) ((c & CONF_DEFAULT_LOCATION_MASK) >> CONF_DEFAULT_LOCATION_SHIFT) +#define CONF_DEFAULT_CONNECTIVITY(c) ((c & CONF_DEFAULT_CONNECTIVITY_MASK) >> CONF_DEFAULT_CONNECTIVITY_SHIFT) + #endif /* HDA_CODEC_H */ 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 bb79560821..3512e40295 100644 --- a/src/add-ons/kernel/drivers/audio/hda/hda_controller.cpp +++ b/src/add-ons/kernel/drivers/audio/hda/hda_controller.cpp @@ -438,7 +438,8 @@ hda_stream_new(hda_audio_group* audioGroup, int type) return stream; } - dprintf("hda: hda_audio_group_get_widgets failed\n"); + dprintf("hda: hda_audio_group_get_widgets failed for %s stream\n", + type == STREAM_PLAYBACK ? " playback" : "record"); free(stream); return NULL; @@ -607,8 +608,8 @@ hda_stream_setup_buffers(hda_audio_group* audioGroup, hda_stream* stream, } } - dprintf("IRA: %s: setup stream %ld: SR=%ld, SF=%ld\n", __func__, stream->id, - stream->rate, stream->bps); + dprintf("IRA: %s: setup stream %ld: SR=%ld, SF=%ld F=0x%x\n", __func__, stream->id, + stream->rate, stream->bps, format); stream->Write16(HDAC_STREAM_FORMAT, format); stream->Write32(HDAC_STREAM_BUFFERS_BASE_LOWER, @@ -633,12 +634,10 @@ hda_stream_setup_buffers(hda_audio_group* audioGroup, hda_stream* stream, verb[0] = MAKE_VERB(codec->addr, stream->io_widgets[i], VID_SET_CONVERTER_FORMAT, format); uint32 val = stream->id << 4; - if (stream->type == STREAM_RECORD) { - if (channelNum < stream->num_channels) - val |= channelNum; - else - val = 0; - } + if (channelNum < stream->num_channels) + val |= channelNum; + else + val = 0; verb[1] = MAKE_VERB(codec->addr, stream->io_widgets[i], VID_SET_CONVERTER_STREAM_CHANNEL, val); hda_send_verbs(codec, verb, response, 2); 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 b8b7393302..343d56ec95 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 @@ -8,7 +8,7 @@ */ -#include "multi_audio.h" +#include "hmulti_audio.h" #include "driver.h" @@ -16,12 +16,18 @@ # undef TRACE #endif +//#define TRACE_MULTI_AUDIO #ifdef TRACE_MULTI_AUDIO # define TRACE(a...) dprintf("\33[34mhda:\33[0m " a) #else # define TRACE(a...) ; #endif +typedef enum { + B_MIX_GAIN = 1 << 0, + B_MIX_MUTE = 1 << 1 +} mixer_type; + static multi_channel_info sChannels[] = { { 0, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 }, @@ -185,10 +191,278 @@ set_global_format(hda_audio_group* audioGroup, multi_format_info* data) } -static status_t -list_mix_controls(hda_audio_group* audioGroup, multi_mix_control_info* data) +static int32 +hda_create_group_control(hda_multi *multi, uint32 *index, int32 parent, + enum strind_id string, const char* name) { + uint32 i = *index; + (*index)++; + multi->controls[i].mix_control.id = MULTI_CONTROL_FIRSTID + i; + multi->controls[i].mix_control.parent = parent; + multi->controls[i].mix_control.flags = B_MULTI_MIX_GROUP; + multi->controls[i].mix_control.master = MULTI_CONTROL_MASTERID; + multi->controls[i].mix_control.string = string; + if (name) + strcpy(multi->controls[i].mix_control.name, name); + + return multi->controls[i].mix_control.id; +} + + +static void +hda_create_channel_control(hda_multi *multi, uint32 *index, int32 parent, int32 string, + hda_widget& widget, bool input, uint32 capabilities, int32 inputIndex) { + uint32 i = *index, id; + hda_multi_mixer_control control; + + control.nid = widget.node_id; + control.input = input; + control.mute = 0; + control.gain = 0; + control.capabilities = capabilities; + control.index = inputIndex; + control.mix_control.master = MULTI_CONTROL_MASTERID; + control.mix_control.parent = parent; + + if (AMP_CAP_NUM_STEPS(capabilities) >= 1) { + control.mix_control.gain.granularity = AMP_CAP_STEP_SIZE(capabilities); + control.mix_control.gain.min_gain = (0.0 - AMP_CAP_OFFSET(capabilities)) + * control.mix_control.gain.granularity; + control.mix_control.gain.max_gain = (AMP_CAP_NUM_STEPS(capabilities) - AMP_CAP_OFFSET(capabilities)) + * control.mix_control.gain.granularity; + + control.mix_control.id = MULTI_CONTROL_FIRSTID + i; + control.mix_control.flags = B_MULTI_MIX_GAIN; + control.mix_control.string = S_null; + control.type = B_MIX_GAIN; + strcpy(control.mix_control.name, "Gain"); + multi->controls[i++] = control; + id = control.mix_control.id; + + // second channel + control.mix_control.id = MULTI_CONTROL_FIRSTID + i; + control.mix_control.master = id; + multi->controls[i++] = control; + TRACE("control nid %ld %f min %f max %f\n", control.nid, control.mix_control.gain.granularity, + control.mix_control.gain.min_gain, control.mix_control.gain.max_gain); + + } + + if (capabilities & AMP_CAP_MUTE) { + control.mix_control.id = MULTI_CONTROL_FIRSTID + i; + control.mix_control.flags = B_MULTI_MIX_ENABLE; + control.mix_control.string = S_MUTE; + control.type = B_MIX_MUTE; + multi->controls[i++] = control; + TRACE("control nid %ld mute\n", control.nid); + } + + *index = i; +} + + +static enum strind_id +hda_find_multi_string(hda_widget& widget) { - data->control_count = 0; + switch (CONF_DEFAULT_DEVICE(widget.d.pin.config)) { + case PIN_DEV_CD: + return S_CD; + case PIN_DEV_LINE_IN: + case PIN_DEV_LINE_OUT: + return S_LINE; + case PIN_DEV_MIC_IN: + return S_MIC; + case PIN_DEV_AUX: + return S_AUX; + case PIN_DEV_SPDIF_IN: + case PIN_DEV_SPDIF_OUT: + return S_SPDIF; + case PIN_DEV_HEAD_PHONE_OUT: + return S_HEADPHONE; + } + return S_null; +} + +static const char * +hda_find_multi_custom_string(hda_widget& widget) +{ + switch (CONF_DEFAULT_DEVICE(widget.d.pin.config)) { + case PIN_DEV_LINE_IN: + case PIN_DEV_LINE_OUT: + switch (CONF_DEFAULT_COLOR(widget.d.pin.config)) { + case 1: + return "Analog Rear"; + case 2: + return "Analog Side"; + case 3: + return "Line In"; + case 4: + return "Analog Front"; + case 6: + return "Digital SPDIF"; + case 9: + return "Mic in"; + } + break; + case PIN_DEV_SPDIF_IN: + return "SPDIF In"; + case PIN_DEV_SPDIF_OUT: + return "SPDIF Out"; + case PIN_DEV_CD: + return "CD"; + case PIN_DEV_HEAD_PHONE_OUT: + return "Headphones"; + } + return NULL; +} + + +static status_t +hda_create_controls_list(hda_multi *multi) +{ + uint32 index = 0, parent, parent2; + hda_audio_group *audioGroup = multi->group; + + parent = hda_create_group_control(multi, &index, 0, S_OUTPUT, NULL); + + for (uint32 i = 0; i < audioGroup->widget_count; i++) { + hda_widget& complex = audioGroup->widgets[i]; + + if (complex.type != WT_PIN_COMPLEX) + continue; + if (!complex.d.pin.output) + continue; + + TRACE("create complex nid %lu\n", complex.node_id); + + hda_widget &widget = * hda_audio_group_get_widget(audioGroup, complex.inputs[complex.active_input]); + + if (widget.type != WT_AUDIO_MIXER) + continue; + if (widget.flags & WIDGET_FLAG_WIDGET_PATH) + continue; + + TRACE(" create widget nid %lu\n", widget.node_id); + + uint32 capabilities = widget.capabilities.output_amplifier; + if (AMP_CAP_NUM_STEPS(capabilities) >= 1) { + + parent2 = hda_create_group_control(multi, &index, + parent, S_null, hda_find_multi_custom_string(complex)); + hda_create_channel_control(multi, &index, parent2, 0, + widget, false, capabilities, 0); + + if ((widget.capabilities.input_amplifier & AMP_CAP_MUTE) + && (AMP_CAP_NUM_STEPS(widget.capabilities.input_amplifier) < 1) + && ((capabilities & AMP_CAP_MUTE) == 0)) { + hda_create_channel_control(multi, &index, parent2, 0, + widget, true, widget.capabilities.input_amplifier, 0); + widget.flags |= WIDGET_FLAG_WIDGET_PATH; + continue; + } + } + + if (AMP_CAP_NUM_STEPS(widget.capabilities.input_amplifier) >= 1) { + + for (uint32 j = 0; j < widget.num_inputs; j++) { + TRACE(" create widget input nid %lu\n", widget.inputs[j]); + parent2 = hda_create_group_control(multi, &index, + parent, hda_find_multi_string(widget), "Output"); + hda_create_channel_control(multi, &index, parent2, 0, + widget, true, widget.capabilities.input_amplifier, j); + } + } + + widget.flags |= WIDGET_FLAG_WIDGET_PATH; + } + + for (uint32 i = 0; i < audioGroup->widget_count; i++) { + hda_widget& widget = audioGroup->widgets[i]; + + if (widget.type != WT_AUDIO_MIXER) + continue; + if (widget.flags & WIDGET_FLAG_WIDGET_PATH) + continue; + + TRACE("create widget nid %lu\n", widget.node_id); + + uint32 capabilities = widget.capabilities.output_amplifier; + if (AMP_CAP_NUM_STEPS(capabilities) >= 1) { + + parent2 = hda_create_group_control(multi, &index, + parent, hda_find_multi_string(widget), "Output"); + hda_create_channel_control(multi, &index, parent2, 0, + widget, false, capabilities, 0); + + if ((widget.capabilities.input_amplifier & AMP_CAP_MUTE) + && (AMP_CAP_NUM_STEPS(widget.capabilities.input_amplifier) < 1) + && ((capabilities & AMP_CAP_MUTE) == 0)) { + hda_create_channel_control(multi, &index, parent2, 0, + widget, true, widget.capabilities.input_amplifier, 0); + widget.flags |= WIDGET_FLAG_WIDGET_PATH; + continue; + } + } + + if (AMP_CAP_NUM_STEPS(widget.capabilities.input_amplifier) >= 1) { + + for (uint32 j = 0; j < widget.num_inputs; j++) { + hda_widget *complex = hda_audio_group_get_widget(audioGroup, widget.inputs[j]); + if (complex->type != WT_PIN_COMPLEX) + continue; + if (!complex->d.pin.output) + continue; + TRACE(" create widget input nid %lu\n", widget.inputs[j]); + parent2 = hda_create_group_control(multi, &index, + parent, S_null, hda_find_multi_custom_string(*complex)); + hda_create_channel_control(multi, &index, parent2, 0, + widget, true, widget.capabilities.input_amplifier, j); + } + } + + widget.flags |= WIDGET_FLAG_WIDGET_PATH; + } + + parent = hda_create_group_control(multi, &index, 0, S_INPUT, NULL); + + for (uint32 i = 0; i < audioGroup->widget_count; i++) { + hda_widget& widget = audioGroup->widgets[i]; + + if (widget.type != WT_AUDIO_INPUT) + continue; + + uint32 capabilities = widget.capabilities.input_amplifier; + if (AMP_CAP_NUM_STEPS(capabilities) < 1) + continue; + + parent2 = hda_create_group_control(multi, &index, + parent, hda_find_multi_string(widget), "Input"); + hda_create_channel_control(multi, &index, parent2, 0, + widget, true, capabilities, 0); + } + + multi->control_count = index; + TRACE("multi->control_count %lu\n", multi->control_count); + return B_OK; +} + + +static status_t +list_mix_controls(hda_audio_group* audioGroup, multi_mix_control_info* MMCI) +{ + multi_mix_control *MMC; + uint32 i; + + MMC = MMCI->controls; + if (MMCI->control_count < 24) + return B_ERROR; + + if (hda_create_controls_list(audioGroup->multi) < B_OK) + return B_ERROR; + for (i=0; imulti->control_count; i++) { + MMC[i] = audioGroup->multi->controls[i].mix_control; + } + + MMCI->control_count = audioGroup->multi->control_count; return B_OK; } @@ -209,6 +483,188 @@ list_mix_channels(hda_audio_group* audioGroup, multi_mix_channel_info *data) } +static void +get_control_gain_mute(hda_audio_group* audioGroup, hda_multi_mixer_control *control, uint32 *resp) +{ + uint32 verb[2]; + verb[0] = MAKE_VERB(audioGroup->codec->addr, + control->nid, + VID_GET_AMPLIFIER_GAIN_MUTE, + (control->input ? AMP_GET_INPUT : AMP_GET_OUTPUT) + | AMP_GET_LEFT_CHANNEL | AMP_GET_INPUT_INDEX(control->index)); + verb[1] = MAKE_VERB(audioGroup->codec->addr, + control->nid, + VID_GET_AMPLIFIER_GAIN_MUTE, + (control->input ? AMP_GET_INPUT : AMP_GET_OUTPUT) + | AMP_GET_RIGHT_CHANNEL | AMP_GET_INPUT_INDEX(control->index)); + hda_send_verbs(audioGroup->codec, verb, resp, 2); +} + + +static status_t +get_mix(hda_audio_group* audioGroup, multi_mix_value_info * MMVI) +{ + int32 i; + uint32 id; + hda_multi_mixer_control *control = NULL; + for (i=0; i < MMVI->item_count; i++) { + id = MMVI->values[i].id - MULTI_CONTROL_FIRSTID; + if (id < 0 || id >= audioGroup->multi->control_count) { + dprintf("hda: get_mix : invalid control id requested : %li\n", id); + continue; + } + control = &audioGroup->multi->controls[id]; + + if (control->mix_control.flags & (B_MULTI_MIX_GAIN | B_MULTI_MIX_ENABLE)) { + uint32 resp[2]; + get_control_gain_mute(audioGroup, control, resp); + if (control->mix_control.flags & B_MULTI_MIX_ENABLE) { + MMVI->values[i].enable = (resp[0] & AMP_MUTE) != 0; + TRACE("get_mix: %ld mute: %d\n", control->nid, MMVI->values[i].enable); + } else if (control->mix_control.flags & B_MULTI_MIX_GAIN) { + uint32 value; + if (control->mix_control.master == MULTI_CONTROL_MASTERID) + value = resp[0] & AMP_GAIN_MASK; + else + value = resp[1] & AMP_GAIN_MASK; + MMVI->values[i].gain = (value - AMP_CAP_OFFSET(control->capabilities)) + * AMP_CAP_STEP_SIZE(control->capabilities); + TRACE("get_mix: %ld gain: %f (%ld)\n", control->nid, MMVI->values[i].gain, value); + } + + + } + + /*if (control->mix_control.flags & B_MULTI_MIX_MUX && control->get) { + float values[1]; + control->get(audioGroup, control, values); + MMVI->values[i].mux = (int32)values[0]; + }*/ + } + return B_OK; +} + + +static status_t +set_mix(hda_audio_group* audioGroup, multi_mix_value_info * MMVI) +{ + int32 i; + uint32 id; + hda_multi_mixer_control *control = NULL; + for (i=0; i < MMVI->item_count; i++) { + id = MMVI->values[i].id - MULTI_CONTROL_FIRSTID; + if (id < 0 || id >= audioGroup->multi->control_count) { + dprintf("set_mix : invalid control id requested : %li\n", id); + continue; + } + control = &audioGroup->multi->controls[id]; + + if (control->mix_control.flags & B_MULTI_MIX_ENABLE) { + control->mute = (MMVI->values[i].enable ? AMP_MUTE : 0); + TRACE("set_mix: %ld mute: %lx\n", control->nid, control->mute); + uint32 resp[2]; + get_control_gain_mute(audioGroup, control, resp); + + uint32 verb[2]; + verb[0] = MAKE_VERB(audioGroup->codec->addr, + control->nid, + VID_SET_AMPLIFIER_GAIN_MUTE, + (control->input ? AMP_SET_INPUT : AMP_SET_OUTPUT) + | AMP_SET_LEFT_CHANNEL + | AMP_SET_INPUT_INDEX(control->index) + | control->mute + | resp[0] & AMP_GAIN_MASK); + TRACE("set_mix: sending verb to %ld: %lx %lx %x %lx\n", control->nid, + control->mute, resp[0] & AMP_GAIN_MASK, control->input, + (control->input ? AMP_SET_INPUT : AMP_SET_OUTPUT) + | AMP_SET_LEFT_CHANNEL + | AMP_SET_INPUT_INDEX(control->index) + | control->mute + | resp[0] & AMP_GAIN_MASK); + verb[1] = MAKE_VERB(audioGroup->codec->addr, + control->nid, + VID_SET_AMPLIFIER_GAIN_MUTE, + (control->input ? AMP_SET_INPUT : AMP_SET_OUTPUT) + | AMP_SET_RIGHT_CHANNEL + | AMP_SET_INPUT_INDEX(control->index) + | control->mute + | resp[1] & AMP_GAIN_MASK); + TRACE("set_mix: ctrl2 sending verb to %ld: %lx %lx %x\n", control->nid, + control->mute, resp[1] & AMP_GAIN_MASK, control->input); + hda_send_verbs(audioGroup->codec, verb, NULL, 2); + } else if (control->mix_control.flags & B_MULTI_MIX_GAIN) { + hda_multi_mixer_control *control2 = NULL; + if (i+1item_count) { + id = MMVI->values[i + 1].id - MULTI_CONTROL_FIRSTID; + if (id < 0 || id >= audioGroup->multi->control_count) { + dprintf("set_mix : invalid control id requested : %li\n", id); + } else { + control2 = &audioGroup->multi->controls[id]; + if (control2->mix_control.master != control->mix_control.id) + control2 = NULL; + } + } + + if (control->mix_control.master == MULTI_CONTROL_MASTERID) + control->gain = (uint32)(MMVI->values[i].gain / AMP_CAP_STEP_SIZE(control->capabilities) + + AMP_CAP_OFFSET(control->capabilities)); + + if (control2 && control2->mix_control.master != MULTI_CONTROL_MASTERID) + control2->gain = (uint32)(MMVI->values[i+1].gain / AMP_CAP_STEP_SIZE(control2->capabilities) + + AMP_CAP_OFFSET(control2->capabilities)); + TRACE("set_mix: %ld gain: %lx and %ld gain: %lx\n", + control->nid, control->gain, control2->nid, control2->gain); + uint32 resp[2]; + get_control_gain_mute(audioGroup, control, resp); + control->mute = resp[0] & AMP_MUTE; + if (control2) + control2->mute = resp[1] & AMP_MUTE; + + uint32 verb[2]; + verb[0] = MAKE_VERB(audioGroup->codec->addr, + control->nid, + VID_SET_AMPLIFIER_GAIN_MUTE, + (control->input ? AMP_SET_INPUT : AMP_SET_OUTPUT) + | AMP_SET_LEFT_CHANNEL + | AMP_SET_INPUT_INDEX(control->index) + | (control->mute & AMP_MUTE) + | (control->gain & AMP_GAIN_MASK)); + TRACE("set_mix: sending verb to %ld: %lx %lx %x %lx\n", control->nid, + control->mute, control->gain, control->input, + (control->input ? AMP_SET_INPUT : AMP_SET_OUTPUT) + | AMP_SET_LEFT_CHANNEL + | AMP_SET_INPUT_INDEX(control->index) + | (control->mute & AMP_MUTE) + | (control->gain & AMP_GAIN_MASK)); + if (control2) { + verb[1] = MAKE_VERB(audioGroup->codec->addr, + control2->nid, + VID_SET_AMPLIFIER_GAIN_MUTE, + (control->input ? AMP_SET_INPUT : AMP_SET_OUTPUT) + | AMP_SET_RIGHT_CHANNEL + | AMP_SET_INPUT_INDEX(control->index) + | (control2->mute & AMP_MUTE) + | (control2->gain & AMP_GAIN_MASK)); + TRACE("set_mix: ctrl2 sending verb to %ld: %lx %lx %x\n", control2->nid, + control2->mute, control2->gain, control2->input); + } + hda_send_verbs(audioGroup->codec, verb, NULL, control2 ? 2 : 1); + + if (control2) + i++; + } + + /*if (control->mix_control.flags & B_MULTI_MIX_MUX && control->set) { + float values[1]; + + values[0] = (float)MMVI->values[i].mux; + control->set(card, control->channel, control->type, values); + }*/ + } + return B_OK; +} + + static status_t get_buffers(hda_audio_group* audioGroup, multi_buffer_list* data) { @@ -455,6 +911,11 @@ multi_audio_control(void* cookie, uint32 op, void* arg, size_t len) case B_MULTI_LIST_MIX_CONNECTIONS: return list_mix_connections(audioGroup, (multi_mix_connection_info*)arg); + case B_MULTI_GET_MIX: + return get_mix(audioGroup, (multi_mix_value_info *)arg); + case B_MULTI_SET_MIX: + return set_mix(audioGroup, (multi_mix_value_info *)arg); + case B_MULTI_GET_BUFFERS: return get_buffers(audioGroup, (multi_buffer_list*)arg); @@ -468,8 +929,6 @@ multi_audio_control(void* cookie, uint32 op, void* arg, size_t len) case B_MULTI_GET_EVENT: case B_MULTI_GET_CHANNEL_FORMATS: case B_MULTI_SET_CHANNEL_FORMATS: - case B_MULTI_GET_MIX: - case B_MULTI_SET_MIX: case B_MULTI_SET_BUFFERS: case B_MULTI_SET_START_TIME: return B_ERROR;