diff --git a/src/add-ons/kernel/drivers/audio/hda/driver.h b/src/add-ons/kernel/drivers/audio/hda/driver.h index 685a306a44..3a7b1aae65 100644 --- a/src/add-ons/kernel/drivers/audio/hda/driver.h +++ b/src/add-ons/kernel/drivers/audio/hda/driver.h @@ -24,13 +24,15 @@ /* values for the class_sub field for class_base = 0x04 (multimedia device) */ #define PCI_hd_audio 3 -#define MAXWORK 16 +#define HDA_MAXAFGS 15 #define HDA_MAXCODECS 15 #define HDA_MAXSTREAMS 16 #define MAX_CODEC_RESPONSES 10 #define MAXINPUTS 32 typedef struct hda_controller_s hda_controller; +typedef struct hda_codec_s hda_codec; +typedef struct hda_afg_s hda_afg; #define STRMAXBUF 10 #define STRMINBUF 2 @@ -40,6 +42,12 @@ enum { STRM_RECORD }; +/* hda_stream_info + * + * This structure describes a single stream of audio data, + * which is can have multiple channels (for stereo or better). + */ + typedef struct hda_stream_info_s { uint32 id; /* HDA controller stream # */ uint32 off; /* HDA I/O/B descriptor offset */ @@ -68,27 +76,29 @@ typedef struct hda_stream_info_s { uint32 bdl_pa; /* BDL physical address */ } hda_stream; -typedef struct hda_codec_s { - uint16 vendor_id; - uint16 product_id; - uint8 hda_rev; - uint16 rev_stepping; - uint8 addr; +/* hda_afg + * + * 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) + * or from an input device to the HDA link (= recording). + */ - sem_id response_sem; - uint32 responses[MAX_CODEC_RESPONSES]; - uint32 response_count; +struct hda_afg_s { + hda_codec* codec; /* Multi Audio API data */ - hda_stream* playback_stream; - hda_stream* record_stream; + hda_stream* playback_stream; + hda_stream* record_stream; + - /* Function Group Data */ - uint32 afg_nid; - uint32 afg_wid_start, afg_wid_count; - uint32 afg_deffmts; - uint32 afg_defrates; - uint32 afg_defpm; + uint32 root_nid, + wid_start, + wid_count; + + uint32 deffmts, + defrates, + defpm; struct { uint32 num_inputs; @@ -116,10 +126,43 @@ typedef struct hda_codec_s { pin_dev_type device; } pin; } d; - } *afg_widgets; + } *widgets; +}; +/* hda_codec + * + * This structure describes a single codec module in the + * HDA compliant device. This is a discrete component, which + * can contain both Audio Function Groups, Modem Function Groups, + * and other customized (vendor specific) Function Groups. + * + * NOTE: Atm, only Audio Function Groups are supported. + */ + +struct hda_codec_s { + uint16 vendor_id; + uint16 product_id; + uint8 hda_rev; + uint16 rev_stepping; + uint8 addr; + + sem_id response_sem; + uint32 responses[MAX_CODEC_RESPONSES]; + uint32 response_count; + + hda_afg* afgs[HDA_MAXAFGS]; + uint32 num_afgs; + struct hda_controller_s* ctrlr; -} hda_codec; +}; + +/* hda_controller + * + * This structure describes a single HDA compliant + * controller. It contains a list of available streams + * for use by the codecs contained, and the messaging queue + * (verb/response) buffers for communication. + */ struct hda_controller_s { pci_info pcii; @@ -149,21 +192,27 @@ struct hda_controller_s { hda_stream* streams[HDA_MAXSTREAMS]; }; +/* driver.c */ extern device_hooks driver_hooks; - extern pci_module_info* pci; - extern hda_controller cards[MAXCARDS]; extern uint32 num_cards; +/* hda_codec.c */ +hda_codec* hda_codec_new(hda_controller* ctrlr, uint32 cad); + +/* hda_multi_audio.c */ +status_t multi_audio_control(void* cookie, uint32 op, void* arg, size_t len); + +/* hda_controller.c: Basic controller support */ status_t hda_hw_init(hda_controller* ctrlr); void hda_hw_stop(hda_controller* ctrlr); void hda_hw_uninit(hda_controller* ctrlr); -hda_codec* hda_codec_new(hda_controller* ctrlr, uint32 cad); status_t hda_send_verbs(hda_codec* codec, corb_t* verbs, uint32* responses, int count); -status_t multi_audio_control(void* cookie, uint32 op, void* arg, size_t len); + +/* hda_controller.c: Stream support */ hda_stream* hda_stream_alloc(hda_controller* ctrlr, int type); -status_t hda_stream_setup_buffers(hda_codec* codec, hda_stream* s, const char* desc); +status_t hda_stream_setup_buffers(hda_afg* afg, hda_stream* s, const char* desc); status_t hda_stream_start(hda_controller* ctrlr, hda_stream* s); status_t hda_stream_stop(hda_controller* ctrlr, hda_stream* s); status_t hda_stream_check_intr(hda_controller* ctrlr, hda_stream* s); diff --git a/src/add-ons/kernel/drivers/audio/hda/hda_codec.c b/src/add-ons/kernel/drivers/audio/hda/hda_codec.c index 3c8f865cc8..9eede8ccf4 100644 --- a/src/add-ons/kernel/drivers/audio/hda/hda_codec.c +++ b/src/add-ons/kernel/drivers/audio/hda/hda_codec.c @@ -101,20 +101,20 @@ hda_widget_get_amplifier_capabilities(hda_codec* codec, uint32 nid) } static status_t -hda_codec_parse_afg(hda_codec* codec, uint32 afg_nid) +hda_codec_parse_afg(hda_afg* afg) { corb_t verbs[6]; uint32 resp[6]; uint32 widx; - hda_widget_get_stream_support(codec, afg_nid, &codec->afg_deffmts, &codec->afg_defrates); - hda_widget_get_pm_support(codec, afg_nid, &codec->afg_defpm); + hda_widget_get_stream_support(afg->codec, afg->root_nid, &afg->deffmts, &afg->defrates); + hda_widget_get_pm_support(afg->codec, afg->root_nid, &afg->defpm); - verbs[0] = MAKE_VERB(codec->addr,afg_nid,VID_GET_PARAM,PID_AUDIO_FG_CAP); - verbs[1] = MAKE_VERB(codec->addr,afg_nid,VID_GET_PARAM,PID_GPIO_COUNT); - verbs[2] = MAKE_VERB(codec->addr,afg_nid,VID_GET_PARAM,PID_SUBORD_NODE_COUNT); + verbs[0] = MAKE_VERB(afg->codec->addr,afg->root_nid,VID_GET_PARAM,PID_AUDIO_FG_CAP); + verbs[1] = MAKE_VERB(afg->codec->addr,afg->root_nid,VID_GET_PARAM,PID_GPIO_COUNT); + verbs[2] = MAKE_VERB(afg->codec->addr,afg->root_nid,VID_GET_PARAM,PID_SUBORD_NODE_COUNT); - if (hda_send_verbs(codec, verbs, resp, 3) == B_OK) { + if (hda_send_verbs(afg->codec, verbs, resp, 3) == B_OK) { dprintf("%s: Output delay: %ld samples, Input delay: %ld samples, Beep Generator: %s\n", __func__, resp[0] & 0xf, (resp[0] >> 8) & 0xf, (resp[0] & (1 << 16)) ? "yes" : "no"); @@ -123,30 +123,27 @@ hda_codec_parse_afg(hda_codec* codec, uint32 afg_nid) (resp[1] & (1 << 30)) ? "yes" : "no", (resp[1] & (1 << 31)) ? "yes" : "no"); - codec->afg_wid_start = resp[2] >> 16; - codec->afg_wid_count = resp[2] & 0xFF; + afg->wid_start = resp[2] >> 16; + afg->wid_count = resp[2] & 0xFF; - codec->afg_widgets = calloc(sizeof(*codec->afg_widgets), codec->afg_wid_count); - if (codec->afg_widgets == NULL) { + afg->widgets = calloc(sizeof(*afg->widgets), afg->wid_count); + if (afg->widgets == NULL) { dprintf("ERROR: Not enough memory!\n"); return B_NO_MEMORY; } - /* Only now mark the AFG as found and used */ - codec->afg_nid = afg_nid; - /* Iterate over all Widgets and collect info */ - for (widx=0; widx < codec->afg_wid_count; widx++) { - uint32 wid = codec->afg_wid_start + widx; + for (widx=0; widx < afg->wid_count; widx++) { + uint32 wid = afg->wid_start + widx; char buf[256]; int off; - verbs[0] = MAKE_VERB(codec->addr,wid,VID_GET_PARAM,PID_AUDIO_WIDGET_CAP); - verbs[1] = MAKE_VERB(codec->addr,wid,VID_GET_PARAM,PID_CONNLIST_LEN); - hda_send_verbs(codec, verbs, resp, 2); + verbs[0] = MAKE_VERB(afg->codec->addr,wid,VID_GET_PARAM,PID_AUDIO_WIDGET_CAP); + verbs[1] = MAKE_VERB(afg->codec->addr,wid,VID_GET_PARAM,PID_CONNLIST_LEN); + hda_send_verbs(afg->codec, verbs, resp, 2); - codec->afg_widgets[widx].type = resp[0] >> 20; - codec->afg_widgets[widx].num_inputs = resp[1] & 0x7F; + afg->widgets[widx].type = resp[0] >> 20; + afg->widgets[widx].num_inputs = resp[1] & 0x7F; off = 0; if (resp[0] & (1 << 11)) off += sprintf(buf+off, "[L-R Swap] "); @@ -161,48 +158,48 @@ hda_codec_parse_afg(hda_codec* codec, uint32 afg_nid) if (resp[0] & (1 << 1)) off += sprintf(buf+off, "[In Amp] "); if (resp[0] & (1 << 0)) off += sprintf(buf+off, "[Stereo] "); - switch(codec->afg_widgets[widx].type) { + switch(afg->widgets[widx].type) { case WT_AUDIO_OUTPUT: dprintf("%ld:\tAudio Output\n", wid); - hda_widget_get_stream_support(codec, wid, - &codec->afg_widgets[widx].d.input.formats, - &codec->afg_widgets[widx].d.input.rates); - hda_widget_get_amplifier_capabilities(codec, wid); + hda_widget_get_stream_support(afg->codec, wid, + &afg->widgets[widx].d.input.formats, + &afg->widgets[widx].d.input.rates); + hda_widget_get_amplifier_capabilities(afg->codec, wid); break; case WT_AUDIO_INPUT: dprintf("%ld:\tAudio Input\n", wid); - hda_widget_get_stream_support(codec, wid, - &codec->afg_widgets[widx].d.input.formats, - &codec->afg_widgets[widx].d.input.rates); - hda_widget_get_amplifier_capabilities(codec, wid); + hda_widget_get_stream_support(afg->codec, wid, + &afg->widgets[widx].d.input.formats, + &afg->widgets[widx].d.input.rates); + hda_widget_get_amplifier_capabilities(afg->codec, wid); break; case WT_AUDIO_MIXER: dprintf("%ld:\tAudio Mixer\n", wid); - hda_widget_get_amplifier_capabilities(codec, wid); + hda_widget_get_amplifier_capabilities(afg->codec, wid); break; case WT_AUDIO_SELECTOR: dprintf("%ld:\tAudio Selector\n", wid); - hda_widget_get_amplifier_capabilities(codec, wid); + hda_widget_get_amplifier_capabilities(afg->codec, wid); break; case WT_PIN_COMPLEX: dprintf("%ld:\tPin Complex\n", wid); - verbs[0] = MAKE_VERB(codec->addr,wid,VID_GET_PARAM,PID_PIN_CAP); - if (hda_send_verbs(codec, verbs, resp, 1) == B_OK) { - codec->afg_widgets[widx].d.pin.input = resp[0] & (1 << 5); - codec->afg_widgets[widx].d.pin.output = resp[0] & (1 << 4); + verbs[0] = MAKE_VERB(afg->codec->addr,wid,VID_GET_PARAM,PID_PIN_CAP); + 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.output = resp[0] & (1 << 4); } - verbs[0] = MAKE_VERB(codec->addr,wid,VID_GET_CFGDEFAULT,0); - if (hda_send_verbs(codec, verbs, resp, 1) == B_OK) { - codec->afg_widgets[widx].d.pin.device = (resp[0] >> 20) & 0xF; + verbs[0] = MAKE_VERB(afg->codec->addr,wid,VID_GET_CFGDEFAULT,0); + if (hda_send_verbs(afg->codec, verbs, resp, 1) == B_OK) { + afg->widgets[widx].d.pin.device = (resp[0] >> 20) & 0xF; dprintf("\t%s, %s, %s, %s\n", portcon[resp[0] >> 30], - defdev[codec->afg_widgets[widx].d.pin.device], + defdev[afg->widgets[widx].d.pin.device], conntype[(resp[0] >> 16) & 0xF], jcolor[(resp[0] >> 12) & 0xF]); } - hda_widget_get_amplifier_capabilities(codec, wid); + hda_widget_get_amplifier_capabilities(afg->codec, wid); break; case WT_POWER: dprintf("%ld:\tPower\n", wid); @@ -222,37 +219,37 @@ hda_codec_parse_afg(hda_codec* codec, uint32 afg_nid) dprintf("\t%s\n", buf); - hda_widget_get_pm_support(codec, wid, &codec->afg_widgets[widx].pm); + hda_widget_get_pm_support(afg->codec, wid, &afg->widgets[widx].pm); - if (codec->afg_widgets[widx].num_inputs) { + if (afg->widgets[widx].num_inputs) { int idx; off = 0; - if (codec->afg_widgets[widx].num_inputs > 1) { - verbs[0] = MAKE_VERB(codec->addr,wid,VID_GET_CONNSEL,0); - if (hda_send_verbs(codec, verbs, resp, 1) == B_OK) - codec->afg_widgets[widx].active_input = resp[0] & 0xFF; + if (afg->widgets[widx].num_inputs > 1) { + verbs[0] = MAKE_VERB(afg->codec->addr,wid,VID_GET_CONNSEL,0); + if (hda_send_verbs(afg->codec, verbs, resp, 1) == B_OK) + afg->widgets[widx].active_input = resp[0] & 0xFF; else - codec->afg_widgets[widx].active_input = -1; + afg->widgets[widx].active_input = -1; } else - codec->afg_widgets[widx].active_input = -1; + afg->widgets[widx].active_input = -1; - for (idx=0; idx < codec->afg_widgets[widx].num_inputs; idx ++) { + for (idx=0; idx < afg->widgets[widx].num_inputs; idx ++) { if (!(idx % 4)) { - verbs[0] = MAKE_VERB(codec->addr,wid,VID_GET_CONNLENTRY,idx); - if (hda_send_verbs(codec, verbs, resp, 1) != B_OK) { + verbs[0] = MAKE_VERB(afg->codec->addr,wid,VID_GET_CONNLENTRY,idx); + if (hda_send_verbs(afg->codec, verbs, resp, 1) != B_OK) { dprintf("%s: Error parsing inputs for widget %ld!\n", __func__, wid); break; } } - if (idx != codec->afg_widgets[widx].active_input) + if (idx != afg->widgets[widx].active_input) off += sprintf(buf+off, "%ld ", (resp[0] >> (8*(idx%4))) & 0xFF); else off += sprintf(buf+off, "(%ld) ", (resp[0] >> (8*(idx%4))) & 0xFF); - codec->afg_widgets[widx].inputs[idx] = (resp[0] >> (8*(idx%4))) & 0xFF; + afg->widgets[widx].inputs[idx] = (resp[0] >> (8*(idx%4))) & 0xFF; } dprintf("\t[ %s]\n", buf); @@ -264,32 +261,32 @@ hda_codec_parse_afg(hda_codec* codec, uint32 afg_nid) } static uint32 -hda_codec_afg_find_dac_path(hda_codec* codec, uint32 wid, uint32 depth) +hda_codec_afg_find_dac_path(hda_afg* afg, uint32 wid, uint32 depth) { - int widx = wid - codec->afg_wid_start; + int widx = wid - afg->wid_start; int idx; - switch(codec->afg_widgets[widx].type) { + switch(afg->widgets[widx].type) { case WT_AUDIO_OUTPUT: return wid; case WT_AUDIO_MIXER: - for (idx=0; idx < codec->afg_widgets[widx].num_inputs; idx++) { - if (hda_codec_afg_find_dac_path(codec, codec->afg_widgets[widx].inputs[idx], depth +1)) { - if (codec->afg_widgets[widx].active_input == -1) - codec->afg_widgets[widx].active_input = 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 (afg->widgets[widx].active_input == -1) + afg->widgets[widx].active_input = idx; - return codec->afg_widgets[widx].inputs[idx]; + return afg->widgets[widx].inputs[idx]; } } break; case WT_AUDIO_SELECTOR: { - int idx = codec->afg_widgets[widx].active_input; + int idx = afg->widgets[widx].active_input; if (idx != -1) { - uint32 wid = codec->afg_widgets[widx].inputs[idx]; - if (hda_codec_afg_find_dac_path(codec, wid, depth +1)) { + uint32 wid = afg->widgets[widx].inputs[idx]; + if (hda_codec_afg_find_dac_path(afg, wid, depth +1)) { return wid; } } @@ -303,41 +300,52 @@ hda_codec_afg_find_dac_path(hda_codec* codec, uint32 wid, uint32 depth) return 0; } -static void -hda_codec_audiofg_new(hda_codec* codec, uint32 afg_nid) +static status_t +hda_codec_afg_new(hda_codec* codec, uint32 afg_nid) { + hda_afg* afg; + status_t rc; uint32 idx; - - /* FIXME: Bail if this isn't the first Audio Function Group we find... */ - if (codec->afg_nid != 0) { - dprintf("SORRY: This driver currently only supports a single Audio Function Group!\n"); - return; + + if ((afg=calloc(1, sizeof(hda_afg))) == NULL) { + rc = B_NO_MEMORY; + goto done; } + /* Setup minimal info needed by hda_codec_parse_afg */ + afg->root_nid = afg_nid; + afg->codec = codec; + /* Parse all widgets in Audio Function Group */ - hda_codec_parse_afg(codec, afg_nid); + rc = hda_codec_parse_afg(afg); + if (rc != B_OK) + goto free_afg; + + /* Setup for worst-case scenario; + we cannot find any output Pin Widgets */ + rc = ENODEV; /* Try to locate all output channels */ - for (idx=0; idx < codec->afg_wid_count; idx++) { + for (idx=0; idx < afg->wid_count; idx++) { uint32 iidx, output_wid = 0; - if (codec->afg_widgets[idx].type != WT_PIN_COMPLEX) + if (afg->widgets[idx].type != WT_PIN_COMPLEX) continue; - if (codec->afg_widgets[idx].d.pin.output) + if (afg->widgets[idx].d.pin.output) continue; - if (codec->afg_widgets[idx].d.pin.device != PIN_DEV_HP_OUT && - codec->afg_widgets[idx].d.pin.device != PIN_DEV_SPEAKER && - codec->afg_widgets[idx].d.pin.device != PIN_DEV_LINE_OUT) + if (afg->widgets[idx].d.pin.device != PIN_DEV_HP_OUT && + afg->widgets[idx].d.pin.device != PIN_DEV_SPEAKER && + afg->widgets[idx].d.pin.device != PIN_DEV_LINE_OUT) continue; - iidx = codec->afg_widgets[idx].active_input; + iidx = afg->widgets[idx].active_input; if (iidx != -1) { - output_wid = hda_codec_afg_find_dac_path(codec, codec->afg_widgets[idx].inputs[iidx], 0); + output_wid = hda_codec_afg_find_dac_path(afg, afg->widgets[idx].inputs[iidx], 0); } else { - for (iidx=0; iidx < codec->afg_widgets[idx].num_inputs; iidx++) { - output_wid = hda_codec_afg_find_dac_path(codec, codec->afg_widgets[idx].inputs[iidx], 0); + 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); if (output_wid) { - corb_t verb = MAKE_VERB(codec->addr,idx+codec->afg_wid_start,VID_SET_CONNSEL,iidx); + 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: Setting output selector failed!\n", __func__); break; @@ -348,25 +356,43 @@ hda_codec_audiofg_new(hda_codec* codec, uint32 afg_nid) if (output_wid) { corb_t verb; - codec->playback_stream->pin_wid = idx + codec->afg_wid_start; - codec->playback_stream->io_wid = output_wid; + /* Setup playback/record streams for Multi Audio API */ + afg->playback_stream = hda_stream_alloc(afg->codec->ctrlr, STRM_PLAYBACK); + afg->record_stream = hda_stream_alloc(afg->codec->ctrlr, STRM_RECORD); + + afg->playback_stream->pin_wid = idx + afg->wid_start; + afg->playback_stream->io_wid = output_wid; dprintf("%s: Found output PIN (%s) connected to output CONV wid:%ld\n", - __func__, defdev[codec->afg_widgets[idx].d.pin.device], output_wid); + __func__, defdev[afg->widgets[idx].d.pin.device], output_wid); /* FIXME: Force Pin Widget to unmute */ - verb = MAKE_VERB(codec->addr, codec->playback_stream->pin_wid, + 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); + + rc = B_OK; break; } } + + /* If we found any valid output channels, we're in the clear */ + if (rc == B_OK) { + codec->afgs[codec->num_afgs++] = afg; + goto done; + } + +free_afg: + free(afg); + +done: + return rc; } hda_codec* hda_codec_new(hda_controller* ctrlr, uint32 cad) { - hda_codec* codec = calloc(sizeof(hda_codec),1); + hda_codec* codec = calloc(1, sizeof(hda_codec)); if (codec) { uint32 responses[3]; corb_t verbs[3]; @@ -377,13 +403,8 @@ hda_codec_new(hda_controller* ctrlr, uint32 cad) codec->addr = cad; codec->response_count = 0; codec->response_sem = create_sem(0, "hda_codec_response_sem"); - codec->afg_nid = 0; ctrlr->codecs[cad] = codec; - /* Setup playback/record streams for Multi Audio API */ - codec->playback_stream = hda_stream_alloc(ctrlr, STRM_PLAYBACK); - codec->record_stream = hda_stream_alloc(ctrlr, STRM_RECORD); - 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); @@ -398,11 +419,13 @@ hda_codec_new(hda_controller* ctrlr, uint32 cad) 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 && (resp&0xFF) == 1) { + if ((rc=hda_send_verbs(codec, verbs, &resp, 1)) == B_OK && + (resp&0xFF) == 1 && + (rc=hda_codec_afg_new(codec, nid)) == B_OK) { /* Found an Audio Function Group! */ - hda_codec_audiofg_new(codec, nid); - } else + } else { dprintf("%s: FG %ld: %s\n", __func__, nid, strerror(rc)); + } } } } diff --git a/src/add-ons/kernel/drivers/audio/hda/hda_controller.c b/src/add-ons/kernel/drivers/audio/hda/hda_controller.c index a53a95da03..9ba8046c16 100644 --- a/src/add-ons/kernel/drivers/audio/hda/hda_controller.c +++ b/src/add-ons/kernel/drivers/audio/hda/hda_controller.c @@ -93,7 +93,7 @@ hda_stream_stop(hda_controller* ctrlr, hda_stream* s) } status_t -hda_stream_setup_buffers(hda_codec* codec, hda_stream* s, const char* desc) +hda_stream_setup_buffers(hda_afg* afg, hda_stream* s, const char* desc) { uint32 buffer_size, buffer_pa, alloc; uint32 response[2], idx; @@ -200,17 +200,17 @@ hda_stream_setup_buffers(hda_codec* codec, hda_stream* s, const char* desc) default: dprintf("%s: Invalid sample rate: 0x%lx\n", __func__, s->samplerate); break; } - OREG16(codec->ctrlr,s->off,FMT) = wfmt; - OREG32(codec->ctrlr,s->off,BDPL) = s->bdl_pa; - OREG32(codec->ctrlr,s->off,BDPU) = 0; - OREG16(codec->ctrlr,s->off,LVI) = s->num_buffers -1; - OREG32(codec->ctrlr,s->off,CBL) = s->num_channels * s->num_buffers; - OREG8(codec->ctrlr,s->off,CTL0) = CTL0_IOCE | CTL0_FEIE | CTL0_DEIE; - OREG8(codec->ctrlr,s->off,CTL2) = s->id << 4; + OREG16(afg->codec->ctrlr,s->off,FMT) = wfmt; + OREG32(afg->codec->ctrlr,s->off,BDPL) = s->bdl_pa; + OREG32(afg->codec->ctrlr,s->off,BDPU) = 0; + OREG16(afg->codec->ctrlr,s->off,LVI) = s->num_buffers -1; + OREG32(afg->codec->ctrlr,s->off,CBL) = s->num_channels * s->num_buffers; + OREG8(afg->codec->ctrlr,s->off,CTL0) = CTL0_IOCE | CTL0_FEIE | CTL0_DEIE; + OREG8(afg->codec->ctrlr,s->off,CTL2) = s->id << 4; - verb[0] = MAKE_VERB(codec->addr, s->io_wid, VID_SET_CONVFORMAT, wfmt); - verb[1] = MAKE_VERB(codec->addr, s->io_wid, VID_SET_CVTSTRCHN, s->id << 4); - rc = hda_send_verbs(codec, verb, response, 2); + verb[0] = MAKE_VERB(afg->codec->addr, s->io_wid, VID_SET_CONVFORMAT, wfmt); + verb[1] = MAKE_VERB(afg->codec->addr, s->io_wid, VID_SET_CVTSTRCHN, s->id << 4); + rc = hda_send_verbs(afg->codec, verb, response, 2); return rc; } diff --git a/src/add-ons/kernel/drivers/audio/hda/hda_multi_audio.c b/src/add-ons/kernel/drivers/audio/hda/hda_multi_audio.c index 6241433c9e..8a749f52e5 100644 --- a/src/add-ons/kernel/drivers/audio/hda/hda_multi_audio.c +++ b/src/add-ons/kernel/drivers/audio/hda/hda_multi_audio.c @@ -34,7 +34,7 @@ format2size(uint32 format) } static status_t -get_description(hda_codec* codec, multi_description* data) +get_description(hda_afg* afg, multi_description* data) { data->interface_version = B_CURRENT_INTERFACE_VERSION; data->interface_minimum = B_CURRENT_INTERFACE_VERSION; @@ -55,8 +55,8 @@ get_description(hda_codec* codec, multi_description* data) } /* determine output/input rates */ - data->output_rates = codec->afg_defrates; - data->input_rates = codec->afg_defrates; + data->output_rates = afg->defrates; + data->input_rates = afg->defrates; /* force existance of 48kHz if variable rates are not supported */ if (data->output_rates == 0) @@ -67,8 +67,8 @@ get_description(hda_codec* codec, multi_description* data) data->max_cvsr_rate = 0; data->min_cvsr_rate = 0; - data->output_formats = codec->afg_deffmts; - data->input_formats = codec->afg_deffmts; + data->output_formats = afg->deffmts; + data->input_formats = afg->deffmts; data->lock_sources = B_MULTI_LOCK_INTERNAL; data->timecode_sources = 0; data->interface_flags = B_MULTI_INTERFACE_PLAYBACK /* | B_MULTI_INTERFACE_RECORD */; @@ -80,7 +80,7 @@ get_description(hda_codec* codec, multi_description* data) } static status_t -get_enabled_channels(hda_codec* codec, multi_channel_enable* data) +get_enabled_channels(hda_afg* afg, multi_channel_enable* data) { B_SET_CHANNEL(data->enable_bits, 0, true); B_SET_CHANNEL(data->enable_bits, 1, true); @@ -92,59 +92,59 @@ get_enabled_channels(hda_codec* codec, multi_channel_enable* data) } static status_t -get_global_format(hda_codec* codec, multi_format_info* data) +get_global_format(hda_afg* afg, multi_format_info* data) { data->output_latency = 0; data->input_latency = 0; data->timecode_kind = 0; - data->output.format = codec->playback_stream->sampleformat; - data->output.rate = codec->playback_stream->samplerate; + data->output.format = afg->playback_stream->sampleformat; + data->output.rate = afg->playback_stream->samplerate; - data->input.format = codec->record_stream->sampleformat; - data->input.rate = codec->record_stream->sampleformat; + data->input.format = afg->record_stream->sampleformat; + data->input.rate = afg->record_stream->sampleformat; return B_OK; } static status_t -set_global_format(hda_codec* codec, multi_format_info* data) +set_global_format(hda_afg* afg, multi_format_info* data) { - codec->playback_stream->sampleformat = data->output.format; - codec->playback_stream->samplerate = data->output.rate; - codec->playback_stream->sample_size = format2size(codec->playback_stream->sampleformat); + afg->playback_stream->sampleformat = data->output.format; + afg->playback_stream->samplerate = data->output.rate; + afg->playback_stream->sample_size = format2size(afg->playback_stream->sampleformat); - codec->record_stream->samplerate = data->input.rate; - codec->record_stream->sampleformat = data->input.format; - codec->record_stream->sample_size = format2size(codec->record_stream->sampleformat); + afg->record_stream->samplerate = data->input.rate; + afg->record_stream->sampleformat = data->input.format; + afg->record_stream->sample_size = format2size(afg->record_stream->sampleformat); return B_OK; } static status_t -list_mix_controls(hda_codec* codec, multi_mix_control_info * data) +list_mix_controls(hda_afg* afg, multi_mix_control_info * data) { return B_OK; } static status_t -list_mix_connections(hda_codec* codec, multi_mix_connection_info * data) +list_mix_connections(hda_afg* afg, multi_mix_connection_info * data) { data->actual_count = 0; return B_OK; } static status_t -list_mix_channels(hda_codec* codec, multi_mix_channel_info *data) +list_mix_channels(hda_afg* afg, multi_mix_channel_info *data) { return B_OK; } static status_t -get_buffers(hda_codec* codec, multi_buffer_list* data) +get_buffers(hda_afg* afg, multi_buffer_list* data) { - uint32 playback_sample_size = codec->playback_stream->sample_size; - uint32 record_sample_size = codec->record_stream->sample_size; + uint32 playback_sample_size = afg->playback_stream->sample_size; + uint32 record_sample_size = afg->record_stream->sample_size; uint32 cidx, bidx; status_t rc; @@ -163,18 +163,18 @@ get_buffers(hda_codec* codec, multi_buffer_list* data) data->flags = 0; /* Copy the requested settings into the streams */ - codec->playback_stream->num_buffers = data->request_playback_buffers; - codec->playback_stream->num_channels = data->request_playback_channels; - codec->playback_stream->buffer_length = data->request_playback_buffer_size; - if ((rc=hda_stream_setup_buffers(codec, codec->playback_stream, "Playback")) != B_OK) { + afg->playback_stream->num_buffers = data->request_playback_buffers; + afg->playback_stream->num_channels = data->request_playback_channels; + afg->playback_stream->buffer_length = data->request_playback_buffer_size; + if ((rc=hda_stream_setup_buffers(afg, afg->playback_stream, "Playback")) != B_OK) { dprintf("%s: Error setting up playback buffers (%s)\n", __func__, strerror(rc)); return rc; } - codec->record_stream->num_buffers = data->request_record_buffers; - codec->record_stream->num_channels = data->request_record_channels; - codec->record_stream->buffer_length = data->request_record_buffer_size; - if ((rc=hda_stream_setup_buffers(codec, codec->record_stream, "Recording")) != B_OK) { + afg->record_stream->num_buffers = data->request_record_buffers; + afg->record_stream->num_channels = data->request_record_channels; + afg->record_stream->buffer_length = data->request_record_buffer_size; + if ((rc=hda_stream_setup_buffers(afg, afg->record_stream, "Recording")) != B_OK) { dprintf("%s: Error setting up recording buffers (%s)\n", __func__, strerror(rc)); return rc; } @@ -186,7 +186,7 @@ get_buffers(hda_codec* codec, multi_buffer_list* data) for (bidx=0; bidx < data->return_playback_buffers; bidx++) { for (cidx=0; cidx < data->return_playback_channels; cidx++) { - data->playback_buffers[bidx][cidx].base = codec->playback_stream->buffers[bidx] + (playback_sample_size * cidx); + data->playback_buffers[bidx][cidx].base = afg->playback_stream->buffers[bidx] + (playback_sample_size * cidx); data->playback_buffers[bidx][cidx].stride = playback_sample_size * data->return_playback_channels; } } @@ -197,7 +197,7 @@ get_buffers(hda_codec* codec, multi_buffer_list* data) for (bidx=0; bidx < data->return_record_buffers; bidx++) { for (cidx=0; cidx < data->return_record_channels; cidx++) { - data->record_buffers[bidx][cidx].base = codec->record_stream->buffers[bidx] + (record_sample_size * cidx); + data->record_buffers[bidx][cidx].base = afg->record_stream->buffers[bidx] + (record_sample_size * cidx); data->record_buffers[bidx][cidx].stride = record_sample_size * data->return_record_channels; } } @@ -206,17 +206,17 @@ get_buffers(hda_codec* codec, multi_buffer_list* data) } static status_t -buffer_exchange(hda_codec* codec, multi_buffer_info* data) +buffer_exchange(hda_afg* afg, multi_buffer_info* data) { static int debug_buffers_exchanged = 0; cpu_status status; status_t rc; - if (!codec->playback_stream->running) - hda_stream_start(codec->ctrlr, codec->playback_stream); + if (!afg->playback_stream->running) + hda_stream_start(afg->codec->ctrlr, afg->playback_stream); /* do playback */ - rc=acquire_sem(codec->playback_stream->buffer_ready_sem); + rc=acquire_sem(afg->playback_stream->buffer_ready_sem); if (rc != B_OK) { dprintf("%s: Error waiting for playback buffer to finish (%s)!\n", __func__, strerror(rc)); @@ -224,13 +224,13 @@ buffer_exchange(hda_codec* codec, multi_buffer_info* data) } status = disable_interrupts(); - acquire_spinlock(&codec->playback_stream->lock); + acquire_spinlock(&afg->playback_stream->lock); - data->playback_buffer_cycle = codec->playback_stream->buffer_cycle; - data->played_real_time = codec->playback_stream->real_time; - data->played_frames_count = codec->playback_stream->frames_count; + data->playback_buffer_cycle = afg->playback_stream->buffer_cycle; + data->played_real_time = afg->playback_stream->real_time; + data->played_frames_count = afg->playback_stream->frames_count; - release_spinlock(&codec->playback_stream->lock); + release_spinlock(&afg->playback_stream->lock); restore_interrupts(status); debug_buffers_exchanged++; @@ -242,13 +242,13 @@ buffer_exchange(hda_codec* codec, multi_buffer_info* data) } static status_t -buffer_force_stop(hda_codec* codec) +buffer_force_stop(hda_afg* afg) { - hda_stream_stop(codec->ctrlr, codec->playback_stream); - hda_stream_stop(codec->ctrlr, codec->record_stream); + hda_stream_stop(afg->codec->ctrlr, afg->playback_stream); + hda_stream_stop(afg->codec->ctrlr, afg->record_stream); - delete_sem(codec->playback_stream->buffer_ready_sem); -// delete_sem(codec->record_stream->buffer_ready_sem); + delete_sem(afg->playback_stream->buffer_ready_sem); +// delete_sem(afg->record_stream->buffer_ready_sem); return B_OK; } @@ -257,28 +257,35 @@ status_t multi_audio_control(void* cookie, uint32 op, void* arg, size_t len) { hda_codec* codec = (hda_codec*)cookie; + hda_afg* afg; + + /* FIXME: Make sure we have a valid codec & afg... */ + if (!codec || codec->num_afgs == 0) + return ENODEV; + + afg = codec->afgs[0]; switch(op) { - case B_MULTI_GET_DESCRIPTION: return get_description(codec, arg); + case B_MULTI_GET_DESCRIPTION: return get_description(afg, arg); case B_MULTI_GET_EVENT_INFO: return B_ERROR; case B_MULTI_SET_EVENT_INFO: return B_ERROR; case B_MULTI_GET_EVENT: return B_ERROR; - case B_MULTI_GET_ENABLED_CHANNELS: return get_enabled_channels(codec, arg); + case B_MULTI_GET_ENABLED_CHANNELS: return get_enabled_channels(afg, arg); case B_MULTI_SET_ENABLED_CHANNELS: return B_OK; - case B_MULTI_GET_GLOBAL_FORMAT: return get_global_format(codec, arg); - case B_MULTI_SET_GLOBAL_FORMAT: return set_global_format(codec, arg); + case B_MULTI_GET_GLOBAL_FORMAT: return get_global_format(afg, arg); + case B_MULTI_SET_GLOBAL_FORMAT: return set_global_format(afg, arg); case B_MULTI_GET_CHANNEL_FORMATS: return B_ERROR; case B_MULTI_SET_CHANNEL_FORMATS: return B_ERROR; case B_MULTI_GET_MIX: return B_ERROR; case B_MULTI_SET_MIX: return B_ERROR; - case B_MULTI_LIST_MIX_CHANNELS: return list_mix_channels(codec, arg); - case B_MULTI_LIST_MIX_CONTROLS: return list_mix_controls(codec, arg); - case B_MULTI_LIST_MIX_CONNECTIONS: return list_mix_connections(codec, arg); - case B_MULTI_GET_BUFFERS: return get_buffers(codec, arg); + case B_MULTI_LIST_MIX_CHANNELS: return list_mix_channels(afg, arg); + case B_MULTI_LIST_MIX_CONTROLS: return list_mix_controls(afg, arg); + case B_MULTI_LIST_MIX_CONNECTIONS: return list_mix_connections(afg, arg); + case B_MULTI_GET_BUFFERS: return get_buffers(afg, arg); case B_MULTI_SET_BUFFERS: return B_ERROR; case B_MULTI_SET_START_TIME: return B_ERROR; - case B_MULTI_BUFFER_EXCHANGE: return buffer_exchange(codec, arg); - case B_MULTI_BUFFER_FORCE_STOP: return buffer_force_stop(codec); + case B_MULTI_BUFFER_EXCHANGE: return buffer_exchange(afg, arg); + case B_MULTI_BUFFER_FORCE_STOP: return buffer_force_stop(afg); } return B_BAD_VALUE;