* Fix some bad error handling

* Plug some leaks
* Initial code for input/output bus discovery



git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21174 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ithamar R. Adema 2007-05-19 16:37:23 +00:00
parent bdf8a2e1e7
commit 641541a4f3
3 changed files with 156 additions and 88 deletions

View File

@ -105,7 +105,7 @@ struct hda_afg_s {
struct { struct {
uint32 num_inputs; uint32 num_inputs;
uint32 active_input; int32 active_input;
uint32 inputs[MAXINPUTS]; uint32 inputs[MAXINPUTS];
uint32 flags; uint32 flags;
@ -124,8 +124,8 @@ struct hda_afg_s {
struct { struct {
} mixer; } mixer;
struct { struct {
uint32 output : 1; uint32 output;
uint32 input : 1; uint32 input;
pin_dev_type device; pin_dev_type device;
} pin; } pin;
} d; } d;

View File

@ -126,7 +126,7 @@ hda_codec_parse_afg(hda_afg* afg)
afg->wid_start = resp[2] >> 16; afg->wid_start = resp[2] >> 16;
afg->wid_count = resp[2] & 0xFF; afg->wid_count = resp[2] & 0xFF;
afg->widgets = calloc(sizeof(*afg->widgets), afg->wid_count); afg->widgets = calloc(afg->wid_count, sizeof(*afg->widgets));
if (afg->widgets == NULL) { if (afg->widgets == NULL) {
dprintf("ERROR: Not enough memory!\n"); dprintf("ERROR: Not enough memory!\n");
return B_NO_MEMORY; return B_NO_MEMORY;
@ -187,6 +187,12 @@ hda_codec_parse_afg(hda_afg* afg)
if (hda_send_verbs(afg->codec, verbs, resp, 1) == B_OK) { if (hda_send_verbs(afg->codec, verbs, resp, 1) == B_OK) {
afg->widgets[widx].d.pin.input = resp[0] & (1 << 5); afg->widgets[widx].d.pin.input = resp[0] & (1 << 5);
afg->widgets[widx].d.pin.output = resp[0] & (1 << 4); afg->widgets[widx].d.pin.output = resp[0] & (1 << 4);
dprintf("\t%s%s\n",
afg->widgets[widx].d.pin.input ? "[Input] " : "",
afg->widgets[widx].d.pin.input ? "[Output]" : "");
} else {
dprintf("%s: Error getting Pin Complex IO\n", __func__);
} }
verbs[0] = MAKE_VERB(afg->codec->addr,wid,VID_GET_CFGDEFAULT,0); verbs[0] = MAKE_VERB(afg->codec->addr,wid,VID_GET_CFGDEFAULT,0);
@ -260,19 +266,22 @@ hda_codec_parse_afg(hda_afg* afg)
return B_OK; return B_OK;
} }
/* hda_codec_afg_find_path
*
* Find path from 'wid' to a widget of type 'wtype', returning its widget id.
* Returns 0 if not found.
*/
static uint32 static uint32
hda_codec_afg_find_dac_path(hda_afg* afg, uint32 wid, uint32 depth) hda_codec_afg_find_path(hda_afg* afg, uint32 wid, uint32 wtype, uint32 depth)
{ {
int widx = wid - afg->wid_start; int widx = wid - afg->wid_start;
int idx; int idx;
switch(afg->widgets[widx].type) { switch(afg->widgets[widx].type) {
case WT_AUDIO_OUTPUT:
return wid;
case WT_AUDIO_MIXER: case WT_AUDIO_MIXER:
for (idx=0; idx < afg->widgets[widx].num_inputs; idx++) { for (idx=0; idx < afg->widgets[widx].num_inputs; idx++) {
if (hda_codec_afg_find_dac_path(afg, afg->widgets[widx].inputs[idx], depth +1)) { if (hda_codec_afg_find_path(afg, afg->widgets[widx].inputs[idx], wtype, depth +1)) {
if (afg->widgets[widx].active_input == -1) if (afg->widgets[widx].active_input == -1)
afg->widgets[widx].active_input = idx; afg->widgets[widx].active_input = idx;
@ -286,7 +295,7 @@ hda_codec_afg_find_dac_path(hda_afg* afg, uint32 wid, uint32 depth)
int idx = afg->widgets[widx].active_input; int idx = afg->widgets[widx].active_input;
if (idx != -1) { if (idx != -1) {
uint32 wid = afg->widgets[widx].inputs[idx]; uint32 wid = afg->widgets[widx].inputs[idx];
if (hda_codec_afg_find_dac_path(afg, wid, depth +1)) { if (hda_codec_afg_find_path(afg, wid, wtype, depth +1)) {
return wid; return wid;
} }
} }
@ -294,6 +303,9 @@ hda_codec_afg_find_dac_path(hda_afg* afg, uint32 wid, uint32 depth)
break; break;
default: default:
if (afg->widgets[widx].type == wtype)
return wid;
break; break;
} }
@ -310,7 +322,9 @@ hda_afg_delete(hda_afg* afg)
if (afg->record_stream != NULL) if (afg->record_stream != NULL)
hda_stream_delete(afg->record_stream); hda_stream_delete(afg->record_stream);
free(afg->widgets); if (afg->widgets)
free(afg->widgets);
free(afg); free(afg);
} }
} }
@ -340,60 +354,99 @@ hda_codec_afg_new(hda_codec* codec, uint32 afg_nid)
we cannot find any output Pin Widgets */ we cannot find any output Pin Widgets */
rc = ENODEV; rc = ENODEV;
/* Try to locate all output channels */ dprintf("%s: Scanning all %ld widgets for outputs/inputs....\n",
__func__, afg->wid_count);
/* Try to locate all input/output channels */
for (idx=0; idx < afg->wid_count; idx++) { for (idx=0; idx < afg->wid_count; idx++) {
uint32 iidx, output_wid = 0; uint32 output_wid = 0, input_wid = 0;
int32 iidx;
if (afg->widgets[idx].type != WT_PIN_COMPLEX) if (afg->widgets[idx].type == WT_PIN_COMPLEX && afg->widgets[idx].d.pin.output) {
continue; if (afg->widgets[idx].d.pin.device != PIN_DEV_HP_OUT &&
if (afg->widgets[idx].d.pin.output) afg->widgets[idx].d.pin.device != PIN_DEV_SPEAKER &&
continue; afg->widgets[idx].d.pin.device != PIN_DEV_LINE_OUT)
if (afg->widgets[idx].d.pin.device != PIN_DEV_HP_OUT && continue;
afg->widgets[idx].d.pin.device != PIN_DEV_SPEAKER &&
afg->widgets[idx].d.pin.device != PIN_DEV_LINE_OUT) iidx = afg->widgets[idx].active_input;
continue; if (iidx != -1) {
output_wid = hda_codec_afg_find_path(afg, afg->widgets[idx].inputs[iidx], WT_AUDIO_OUTPUT, 0);
iidx = afg->widgets[idx].active_input; } else {
if (iidx != -1) { for (iidx=0; iidx < afg->widgets[idx].num_inputs; iidx++) {
output_wid = hda_codec_afg_find_dac_path(afg, afg->widgets[idx].inputs[iidx], 0); output_wid = hda_codec_afg_find_path(afg, afg->widgets[idx].inputs[iidx], WT_AUDIO_OUTPUT, 0);
} else { if (output_wid) {
for (iidx=0; iidx < afg->widgets[idx].num_inputs; iidx++) { corb_t verb = MAKE_VERB(codec->addr,idx+afg->wid_start,VID_SET_CONNSEL,iidx);
output_wid = hda_codec_afg_find_dac_path(afg, afg->widgets[idx].inputs[iidx], 0); if (hda_send_verbs(codec, &verb, NULL, 1) != B_OK)
if (output_wid) { dprintf("%s: Setting output selector failed!\n", __func__);
corb_t verb = MAKE_VERB(codec->addr,idx+afg->wid_start,VID_SET_CONNSEL,iidx); break;
if (hda_send_verbs(codec, &verb, NULL, 1) != B_OK) }
dprintf("%s: Setting output selector failed!\n", __func__);
break;
} }
} }
}
if (output_wid) {
if (!afg->playback_stream) {
corb_t verb;
/* Setup playback/record streams for Multi Audio API */
afg->playback_stream = hda_stream_new(afg->codec->ctrlr, STRM_PLAYBACK);
afg->record_stream = hda_stream_new(afg->codec->ctrlr, STRM_RECORD);
afg->playback_stream->pin_wid = idx + afg->wid_start;
afg->playback_stream->io_wid = output_wid;
/* FIXME: Force Pin Widget to unmute */
verb = MAKE_VERB(codec->addr, afg->playback_stream->pin_wid,
VID_SET_AMPGAINMUTE, (1 << 15) | (1 << 13) | (1 << 12));
hda_send_verbs(codec, &verb, NULL, 1);
}
dprintf("%s: Found output PIN (%s) connected to output CONV wid:%ld\n",
__func__, defdev[afg->widgets[idx].d.pin.device], output_wid);
}
}
if (output_wid) { if (afg->widgets[idx].type == WT_AUDIO_INPUT) {
corb_t verb; iidx = afg->widgets[idx].active_input;
if (iidx != -1) {
/* Setup playback/record streams for Multi Audio API */ input_wid = hda_codec_afg_find_path(afg, afg->widgets[idx].inputs[iidx], WT_PIN_COMPLEX, 0);
afg->playback_stream = hda_stream_new(afg->codec->ctrlr, STRM_PLAYBACK); } else {
afg->record_stream = hda_stream_new(afg->codec->ctrlr, STRM_RECORD); for (iidx=0; iidx < afg->widgets[idx].num_inputs; iidx++) {
input_wid = hda_codec_afg_find_path(afg, afg->widgets[idx].inputs[iidx], WT_PIN_COMPLEX, 0);
afg->playback_stream->pin_wid = idx + afg->wid_start; if (input_wid) {
afg->playback_stream->io_wid = output_wid; corb_t verb = MAKE_VERB(codec->addr,idx+afg->wid_start,VID_SET_CONNSEL,iidx);
if (hda_send_verbs(codec, &verb, NULL, 1) != B_OK)
dprintf("%s: Found output PIN (%s) connected to output CONV wid:%ld\n", dprintf("%s: Setting input selector failed!\n", __func__);
__func__, defdev[afg->widgets[idx].d.pin.device], output_wid); break;
}
/* FIXME: Force Pin Widget to unmute */ }
verb = MAKE_VERB(codec->addr, afg->playback_stream->pin_wid, }
VID_SET_AMPGAINMUTE, (1 << 15) | (1 << 13) | (1 << 12));
hda_send_verbs(codec, &verb, NULL, 1); if (input_wid) {
if (!afg->record_stream) {
rc = B_OK; corb_t verb;
break;
/* Setup playback/record streams for Multi Audio API */
afg->record_stream = hda_stream_new(afg->codec->ctrlr, STRM_RECORD);
afg->record_stream->pin_wid = input_wid;
afg->record_stream->io_wid = idx + afg->wid_start;
/* FIXME: Force Pin Widget to unmute */
verb = MAKE_VERB(codec->addr, afg->record_stream->pin_wid,
VID_SET_AMPGAINMUTE, (1 << 15) | (1 << 13) | (1 << 12));
hda_send_verbs(codec, &verb, NULL, 1);
}
dprintf("%s: Found input PIN (%s) connected to input CONV wid:%ld\n",
__func__, defdev[afg->widgets[input_wid-afg->wid_start].d.pin.device], idx+afg->wid_start);
}
} }
} }
/* If we found any valid output channels, we're in the clear */ /* If we found any valid output channels, we're in the clear */
if (rc == B_OK) { if (afg && afg->playback_stream) {
codec->afgs[codec->num_afgs++] = afg; codec->afgs[codec->num_afgs++] = afg;
rc = B_OK;
goto done; goto done;
} }
@ -425,42 +478,54 @@ hda_codec*
hda_codec_new(hda_controller* ctrlr, uint32 cad) hda_codec_new(hda_controller* ctrlr, uint32 cad)
{ {
hda_codec* codec = calloc(1, sizeof(hda_codec)); hda_codec* codec = calloc(1, sizeof(hda_codec));
if (codec) { uint32 responses[3];
uint32 responses[3]; corb_t verbs[3];
corb_t verbs[3]; status_t rc;
status_t rc; uint32 nid;
uint32 nid;
codec->ctrlr = ctrlr;
codec->addr = cad;
codec->response_count = 0;
codec->response_sem = create_sem(0, "hda_codec_response_sem");
ctrlr->codecs[cad] = codec;
verbs[0] = MAKE_VERB(cad,0,VID_GET_PARAM,PID_VENDORID);
verbs[1] = MAKE_VERB(cad,0,VID_GET_PARAM,PID_REVISIONID);
verbs[2] = MAKE_VERB(cad,0,VID_GET_PARAM,PID_SUBORD_NODE_COUNT);
if (hda_send_verbs(codec, verbs, responses, 3) == B_OK) { if (codec == NULL) goto exit_new;
dprintf("Codec %ld Vendor: %04lx Product: %04lx\n",
cad, responses[0] >> 16, responses[0] & 0xFFFF); codec->ctrlr = ctrlr;
codec->addr = cad;
codec->response_sem = create_sem(0, "hda_codec_response_sem");
ctrlr->codecs[cad] = codec;
for (nid=responses[2] >> 16; verbs[0] = MAKE_VERB(cad,0,VID_GET_PARAM,PID_VENDORID);
nid < (responses[2] >> 16) + (responses[2] & 0xFF); verbs[1] = MAKE_VERB(cad,0,VID_GET_PARAM,PID_REVISIONID);
nid++) { verbs[2] = MAKE_VERB(cad,0,VID_GET_PARAM,PID_SUBORD_NODE_COUNT);
uint32 resp;
verbs[0] = MAKE_VERB(cad,nid,VID_GET_PARAM,PID_FUNCGRP_TYPE); if (hda_send_verbs(codec, verbs, responses, 3) != B_OK)
goto cmd_failed;
dprintf("Codec %ld Vendor: %04lx Product: %04lx\n",
cad, responses[0] >> 16, responses[0] & 0xFFFF);
for (nid=responses[2] >> 16;
nid < (responses[2] >> 16) + (responses[2] & 0xFF);
nid++) {
uint32 resp;
verbs[0] = MAKE_VERB(cad,nid,VID_GET_PARAM,PID_FUNCGRP_TYPE);
if ((rc=hda_send_verbs(codec, verbs, &resp, 1)) == B_OK && if ((rc=hda_send_verbs(codec, verbs, &resp, 1)) != B_OK)
(resp&0xFF) == 1 && goto cmd_failed;
(rc=hda_codec_afg_new(codec, nid)) == B_OK) {
/* Found an Audio Function Group! */ if ((resp&0xFF) == 1) {
} else { /* Found an Audio Function Group! */
dprintf("%s: FG %ld: %s\n", __func__, nid, strerror(rc)); if ((rc=hda_codec_afg_new(codec, nid)) != B_OK) {
} dprintf("%s: Failed to setup new audio function group (%s)!\n",
} __func__, strerror(rc));
goto cmd_failed;
}
} }
} }
goto exit_new;
cmd_failed:
ctrlr->codecs[cad] = NULL;
hda_codec_delete(codec);
codec = NULL;
exit_new:
return codec; return codec;
} }

View File

@ -498,8 +498,11 @@ hda_hw_init(hda_controller* ctrlr)
for (idx=0; idx < HDA_MAXCODECS; idx++) for (idx=0; idx < HDA_MAXCODECS; idx++)
if (ctrlr->codecsts & (1 << idx)) if (ctrlr->codecsts & (1 << idx))
hda_codec_new(ctrlr, idx); hda_codec_new(ctrlr, idx);
return B_OK; if (ctrlr->codecs[0] != NULL)
return B_OK;
else
rc = ENODEV;
corb_rirb_failed: corb_rirb_failed:
REG32(ctrlr,INTCTL) = 0; REG32(ctrlr,INTCTL) = 0;