* Removed B_CAN_INTERRUPT from acquire_sem_etc() call in hda_send_verbs();

that doesn't look right to me (and since there is a 50 ms timeout anyway...).
* Minor coding style cleanup.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@23872 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2008-02-05 11:21:07 +00:00
parent 7b95b37ca2
commit 44f1689702
8 changed files with 907 additions and 655 deletions
+55 -51
View File
@@ -1,110 +1,114 @@
/*
* Copyright 2007-2008, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ithamar Adema, ithamar AT unet DOT nl
*/
#include "driver.h" #include "driver.h"
hda_controller cards[MAXCARDS];
uint32 num_cards;
int32 api_version = B_CUR_DRIVER_API_VERSION; int32 api_version = B_CUR_DRIVER_API_VERSION;
pci_module_info* pci; hda_controller gCards[MAXCARDS];
uint32 gNumCards;
pci_module_info* gPci;
const char** publish_devices(void); /* Just to silence compiler */
status_t status_t
init_hardware(void) init_hardware(void)
{ {
pci_info pcii; pci_info info;
status_t rc;
long i; long i;
if ((rc=get_module(B_PCI_MODULE_NAME, (module_info**)&pci)) == B_OK) { if (get_module(B_PCI_MODULE_NAME, (module_info**)&gPci) != B_OK)
for (i=0; pci->get_nth_pci_info(i,&pcii) == B_OK; i++) { return ENODEV;
if (pcii.class_base == PCI_multimedia && pcii.class_sub == PCI_hd_audio) {
put_module(B_PCI_MODULE_NAME);
pci = NULL;
return B_OK;
}
}
put_module(B_PCI_MODULE_NAME); for (i = 0; gPci->get_nth_pci_info(i, &info) == B_OK; i++) {
if (info.class_base == PCI_multimedia
&& info.class_sub == PCI_hd_audio) {
put_module(B_PCI_MODULE_NAME);
return B_OK;
}
} }
pci = NULL; put_module(B_PCI_MODULE_NAME);
return ENODEV; return ENODEV;
} }
status_t status_t
init_driver (void) init_driver(void)
{ {
char path[B_PATH_NAME_LENGTH]; char path[B_PATH_NAME_LENGTH];
pci_info pcii; pci_info info;
status_t rc;
long i; long i;
num_cards = 0; if (get_module(B_PCI_MODULE_NAME, (module_info**)&gPci) != B_OK)
return ENODEV;
if ((rc=get_module(B_PCI_MODULE_NAME, (module_info**)&pci)) == B_OK) { gNumCards = 0;
for (i=0; pci->get_nth_pci_info(i,&pcii) == B_OK; i++) {
if (pcii.class_base == PCI_multimedia && pcii.class_sub == PCI_hd_audio) { for (i = 0; gPci->get_nth_pci_info(i, &info) == B_OK; i++) {
cards[num_cards].pcii = pcii; if (info.class_base == PCI_multimedia
cards[num_cards].opened = 0; && info.class_sub == PCI_hd_audio) {
sprintf(path, DEVFS_PATH_FORMAT, num_cards); gCards[gNumCards].pci_info = info;
cards[num_cards++].devfs_path = strdup(path); gCards[gNumCards].opened = 0;
sprintf(path, DEVFS_PATH_FORMAT, gNumCards);
dprintf("HDA: Detected controller @ PCI:%d:%d:%d, IRQ:%d, type %04x/%04x\n", gCards[gNumCards++].devfs_path = strdup(path);
pcii.bus, pcii.device, pcii.function,
pcii.u.h0.interrupt_line, dprintf("HDA: Detected controller @ PCI:%d:%d:%d, IRQ:%d, type %04x/%04x\n",
pcii.vendor_id, pcii.device_id); info.bus, info.device, info.function,
} info.u.h0.interrupt_line,
info.vendor_id, info.device_id);
} }
} else {
return rc;
} }
if (num_cards == 0) { if (gNumCards == 0) {
put_module(B_PCI_MODULE_NAME); put_module(B_PCI_MODULE_NAME);
pci = NULL;
return ENODEV; return ENODEV;
} }
return B_OK; return B_OK;
} }
void void
uninit_driver (void) uninit_driver(void)
{ {
long i; long i;
dprintf("IRA: %s\n", __func__); dprintf("IRA: %s\n", __func__);
for (i=0; i < num_cards; i++) {
free((void*)cards[i].devfs_path); for (i = 0; i < gNumCards; i++) {
cards[i].devfs_path = NULL; free((void*)gCards[i].devfs_path);
gCards[i].devfs_path = NULL;
} }
if (pci != NULL) { put_module(B_PCI_MODULE_NAME);
put_module(B_PCI_MODULE_NAME);
pci = NULL;
}
} }
const char** const char**
publish_devices(void) publish_devices(void)
{ {
static const char* devs[MAXCARDS+1]; static const char* devs[MAXCARDS+1];
long i; long i;
dprintf("IRA: %s\n", __func__); dprintf("IRA: %s\n", __func__);
for (i=0; i < num_cards; i++) for (i = 0; i < gNumCards; i++)
devs[i] = cards[i].devfs_path; devs[i] = gCards[i].devfs_path;
devs[i] = NULL; devs[i] = NULL;
return devs; return devs;
} }
device_hooks* device_hooks*
find_device(const char* name) find_device(const char* name)
{ {
dprintf("IRA: %s\n", __func__); dprintf("IRA: %s\n", __func__);
return &driver_hooks; return &gDriverHooks;
} }
+21 -14
View File
@@ -1,3 +1,10 @@
/*
* Copyright 2007-2008, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ithamar Adema, ithamar AT unet DOT nl
*/
#ifndef _HDA_H_ #ifndef _HDA_H_
#define _HDA_H_ #define _HDA_H_
@@ -158,7 +165,7 @@ struct hda_codec_s {
hda_afg* afgs[HDA_MAXAFGS]; hda_afg* afgs[HDA_MAXAFGS];
uint32 num_afgs; uint32 num_afgs;
struct hda_controller_s* ctrlr; struct hda_controller_s* controller;
}; };
/* hda_controller /* hda_controller
@@ -170,7 +177,7 @@ struct hda_codec_s {
*/ */
struct hda_controller_s { struct hda_controller_s {
pci_info pcii; struct pci_info pci_info;
vuint32 opened; vuint32 opened;
const char* devfs_path; const char* devfs_path;
@@ -199,30 +206,30 @@ struct hda_controller_s {
}; };
/* driver.c */ /* driver.c */
extern device_hooks driver_hooks; extern device_hooks gDriverHooks;
extern pci_module_info* pci; extern pci_module_info* gPci;
extern hda_controller cards[MAXCARDS]; extern hda_controller gCards[MAXCARDS];
extern uint32 num_cards; extern uint32 gNumCards;
/* hda_codec.c */ /* hda_codec.c */
hda_codec* hda_codec_new(hda_controller* ctrlr, uint32 cad); hda_codec* hda_codec_new(hda_controller* controller, uint32 cad);
void hda_codec_delete(hda_codec*); void hda_codec_delete(hda_codec*);
/* hda_multi_audio.c */ /* hda_multi_audio.c */
status_t multi_audio_control(void* cookie, uint32 op, void* arg, size_t len); status_t multi_audio_control(void* cookie, uint32 op, void* arg, size_t len);
/* hda_controller.c: Basic controller support */ /* hda_controller.c: Basic controller support */
status_t hda_hw_init(hda_controller* ctrlr); status_t hda_hw_init(hda_controller* controller);
void hda_hw_stop(hda_controller* ctrlr); void hda_hw_stop(hda_controller* controller);
void hda_hw_uninit(hda_controller* ctrlr); void hda_hw_uninit(hda_controller* controller);
status_t hda_send_verbs(hda_codec* codec, corb_t* verbs, uint32* responses, int count); status_t hda_send_verbs(hda_codec* codec, corb_t* verbs, uint32* responses, int count);
/* hda_controller.c: Stream support */ /* hda_controller.c: Stream support */
hda_stream* hda_stream_new(hda_controller* ctrlr, int type); hda_stream* hda_stream_new(hda_controller* controller, int type);
void hda_stream_delete(hda_stream* s); void hda_stream_delete(hda_stream* s);
status_t hda_stream_setup_buffers(hda_afg* afg, 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_start(hda_controller* controller, hda_stream* s);
status_t hda_stream_stop(hda_controller* ctrlr, hda_stream* s); status_t hda_stream_stop(hda_controller* controller, hda_stream* s);
status_t hda_stream_check_intr(hda_controller* ctrlr, hda_stream* s); status_t hda_stream_check_intr(hda_controller* controller, hda_stream* s);
#endif /* _HDA_H_ */ #endif /* _HDA_H_ */
+259 -179
View File
@@ -1,34 +1,47 @@
/*
* Copyright 2007-2008, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ithamar Adema, ithamar AT unet DOT nl
*/
#include "driver.h" #include "driver.h"
#include "hda_codec_defs.h" #include "hda_codec_defs.h"
const char* portcon[] = {
static const char* kPortConnector[] = {
"Jack", "None", "Fixed", "Dual" "Jack", "None", "Fixed", "Dual"
}; };
const char* defdev[] = { static const char* kDefaultDevice[] = {
"Line Out", "Speaker", "HP Out", "CD", "SPDIF out", "Digital Other Out", "Modem Line Side", "Line Out", "Speaker", "HP Out", "CD", "SPDIF out", "Digital Other Out",
"Modem Hand Side", "Line In", "AUX", "Mic In", "Telephony", "SPDIF In", "Digital Other In", "Modem Line Side", "Modem Hand Side", "Line In", "AUX", "Mic In",
"Reserved", "Other" "Telephony", "SPDIF In", "Digital Other In", "Reserved", "Other"
}; };
const char* conntype[] = { static const char* kConnectionType[] = {
"N/A", "1/8\"", "1/4\"", "ATAPI internal", "RCA", "Optical", "Other Digital", "Other Analog", "N/A", "1/8\"", "1/4\"", "ATAPI internal", "RCA", "Optical",
"Multichannel Analog (DIN)", "XLR/Professional", "RJ-11 (Modem)", "Combination", "-", "-", "-", "Other" "Other Digital", "Other Analog", "Multichannel Analog (DIN)",
"XLR/Professional", "RJ-11 (Modem)", "Combination", "-", "-", "-", "Other"
}; };
const char* jcolor[] = { static const char* kJackColor[] = {
"N/A", "Black", "Grey", "Blue", "Green", "Red", "Orange", "Yellow", "Purple", "Pink", "N/A", "Black", "Grey", "Blue", "Green", "Red", "Orange", "Yellow",
"-", "-", "-", "-", "White", "Other" "Purple", "Pink", "-", "-", "-", "-", "White", "Other"
}; };
static status_t static status_t
hda_widget_get_pm_support(hda_codec* codec, uint32 nid, uint32* pm) hda_widget_get_pm_support(hda_codec* codec, uint32 nid, uint32* pm)
{ {
corb_t verb = MAKE_VERB(codec->addr,nid,VID_GET_PARAM,PID_POWERSTATE_SUPPORT); corb_t verb = MAKE_VERB(codec->addr, nid, VID_GET_PARAM,
PID_POWERSTATE_SUPPORT);
status_t rc; status_t rc;
uint32 resp; uint32 resp;
if ((rc=hda_send_verbs(codec, &verb, &resp, 1)) == B_OK) { if ((rc = hda_send_verbs(codec, &verb, &resp, 1)) == B_OK) {
*pm = 0; *pm = 0;
/* FIXME: Define constants for powermanagement modes */ /* FIXME: Define constants for powermanagement modes */
@@ -37,12 +50,14 @@ hda_widget_get_pm_support(hda_codec* codec, uint32 nid, uint32* pm)
if (resp & (1 << 2)) ; if (resp & (1 << 2)) ;
if (resp & (1 << 3)) ; if (resp & (1 << 3)) ;
} }
return rc; return rc;
} }
static status_t static status_t
hda_widget_get_stream_support(hda_codec* codec, uint32 nid, uint32* fmts, uint32* rates) hda_widget_get_stream_support(hda_codec* codec, uint32 nid, uint32* fmts,
uint32* rates)
{ {
corb_t verbs[2]; corb_t verbs[2];
uint32 resp[2]; uint32 resp[2];
@@ -50,56 +65,76 @@ hda_widget_get_stream_support(hda_codec* codec, uint32 nid, uint32* fmts, uint32
verbs[0] = MAKE_VERB(codec->addr,nid,VID_GET_PARAM,PID_STREAM_SUPPORT); verbs[0] = MAKE_VERB(codec->addr,nid,VID_GET_PARAM,PID_STREAM_SUPPORT);
verbs[1] = MAKE_VERB(codec->addr,nid,VID_GET_PARAM,PID_PCM_SUPPORT); verbs[1] = MAKE_VERB(codec->addr,nid,VID_GET_PARAM,PID_PCM_SUPPORT);
if ((rc=hda_send_verbs(codec, verbs, resp, 2)) == B_OK) { if ((rc = hda_send_verbs(codec, verbs, resp, 2)) == B_OK) {
*fmts = 0; *rates = 0; *fmts = 0; *rates = 0;
if (resp[2] & (1 << 0)) { if (resp[2] & (1 << 0)) {
if (resp[1] & (1 << 0)) *rates |= B_SR_8000; if (resp[1] & (1 << 0))
if (resp[1] & (1 << 1)) *rates |= B_SR_11025; *rates |= B_SR_8000;
if (resp[1] & (1 << 2)) *rates |= B_SR_16000; if (resp[1] & (1 << 1))
if (resp[1] & (1 << 3)) *rates |= B_SR_22050; *rates |= B_SR_11025;
if (resp[1] & (1 << 4)) *rates |= B_SR_32000; if (resp[1] & (1 << 2))
if (resp[1] & (1 << 5)) *rates |= B_SR_44100; *rates |= B_SR_16000;
if (resp[1] & (1 << 6)) *rates |= B_SR_48000; if (resp[1] & (1 << 3))
if (resp[1] & (1 << 7)) *rates |= B_SR_88200; *rates |= B_SR_22050;
if (resp[1] & (1 << 8)) *rates |= B_SR_96000; if (resp[1] & (1 << 4))
if (resp[1] & (1 << 9)) *rates |= B_SR_176400; *rates |= B_SR_32000;
if (resp[1] & (1 << 10)) *rates |= B_SR_192000; if (resp[1] & (1 << 5))
if (resp[1] & (1 << 11)) *rates |= B_SR_384000; *rates |= B_SR_44100;
if (resp[1] & (1 << 6))
if (resp[1] & (1<<16)) *fmts |= B_FMT_8BIT_S; *rates |= B_SR_48000;
if (resp[1] & (1<<17)) *fmts |= B_FMT_16BIT; if (resp[1] & (1 << 7))
if (resp[1] & (1<<18)) *fmts |= B_FMT_18BIT; *rates |= B_SR_88200;
if (resp[1] & (1<<19)) *fmts |= B_FMT_24BIT; if (resp[1] & (1 << 8))
if (resp[1] & (1<<20)) *fmts |= B_FMT_32BIT; *rates |= B_SR_96000;
if (resp[1] & (1 << 9))
*rates |= B_SR_176400;
if (resp[1] & (1 << 10))
*rates |= B_SR_192000;
if (resp[1] & (1 << 11))
*rates |= B_SR_384000;
if (resp[1] & (1 << 16))
*fmts |= B_FMT_8BIT_S;
if (resp[1] & (1 << 17))
*fmts |= B_FMT_16BIT;
if (resp[1] & (1 << 18))
*fmts |= B_FMT_18BIT;
if (resp[1] & (1 << 19))
*fmts |= B_FMT_24BIT;
if (resp[1] & (1 << 20))
*fmts |= B_FMT_32BIT;
} }
//FIXME: if (resp[0] & (1 << 1)) *fmts |= B_FMT_FLOAT; //FIXME: if (resp[0] & (1 << 1)) *fmts |= B_FMT_FLOAT;
//FIXME: if (resp[0] & (1 << 2)) /* Sort out how to handle AC3 */; //FIXME: if (resp[0] & (1 << 2)) /* Sort out how to handle AC3 */;
} }
return rc; return rc;
} }
static status_t static status_t
hda_widget_get_amplifier_capabilities(hda_codec* codec, uint32 nid) hda_widget_get_amplifier_capabilities(hda_codec* codec, uint32 nid)
{ {
status_t rc; status_t rc;
corb_t verb; corb_t verb;
uint32 resp; uint32 resp;
verb = MAKE_VERB(codec->addr,nid,VID_GET_PARAM,PID_OUTPUT_AMP_CAP); verb = MAKE_VERB(codec->addr, nid, VID_GET_PARAM, PID_OUTPUT_AMP_CAP);
if ((rc=hda_send_verbs(codec, &verb, &resp, 1)) == B_OK && resp != 0) { rc = hda_send_verbs(codec, &verb, &resp, 1);
if (rc == B_OK && resp != 0) {
dprintf("\tAMP: Mute: %s, step size: %ld, # steps: %ld, offset: %ld\n", dprintf("\tAMP: Mute: %s, step size: %ld, # steps: %ld, offset: %ld\n",
(resp & (1 << 31)) ? "supported" : "N/A", (resp & (1 << 31)) ? "supported" : "N/A",
(resp >> 16) & 0x7F, (resp >> 16) & 0x7F,
(resp >> 8) & 0x7F, (resp >> 8) & 0x7F,
resp & 0x7F); resp & 0x7F);
} }
return rc; return rc;
} }
static status_t static status_t
hda_codec_parse_afg(hda_afg* afg) hda_codec_parse_afg(hda_afg* afg)
{ {
@@ -107,20 +142,25 @@ hda_codec_parse_afg(hda_afg* afg)
uint32 resp[6]; uint32 resp[6];
uint32 widx; uint32 widx;
hda_widget_get_stream_support(afg->codec, afg->root_nid, &afg->deffmts, &afg->defrates); 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); hda_widget_get_pm_support(afg->codec, afg->root_nid, &afg->defpm);
verbs[0] = MAKE_VERB(afg->codec->addr,afg->root_nid,VID_GET_PARAM,PID_AUDIO_FG_CAP); verbs[0] = MAKE_VERB(afg->codec->addr, afg->root_nid, VID_GET_PARAM,
verbs[1] = MAKE_VERB(afg->codec->addr,afg->root_nid,VID_GET_PARAM,PID_GPIO_COUNT); PID_AUDIO_FG_CAP);
verbs[2] = MAKE_VERB(afg->codec->addr,afg->root_nid,VID_GET_PARAM,PID_SUBORD_NODE_COUNT); 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(afg->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__, dprintf("%s: Output delay: %ld samples, Input delay: %ld samples, "
resp[0] & 0xf, (resp[0] >> 8) & 0xf, (resp[0] & (1 << 16)) ? "yes" : "no"); "Beep Generator: %s\n", __func__, resp[0] & 0xf,
(resp[0] >> 8) & 0xf, (resp[0] & (1 << 16)) ? "yes" : "no");
dprintf("%s: #GPIO: %ld, #GPO: %ld, #GPI: %ld, unsol: %s, wake: %s\n", __func__, dprintf("%s: #GPIO: %ld, #GPO: %ld, #GPI: %ld, unsol: %s, wake: %s\n",
resp[4] & 0xFF, (resp[1] >> 8) & 0xFF, (resp[1] >> 16) & 0xFF, __func__, resp[4] & 0xFF, (resp[1] >> 8) & 0xFF,
(resp[1] & (1 << 30)) ? "yes" : "no", (resp[1] >> 16) & 0xFF, (resp[1] & (1 << 30)) ? "yes" : "no",
(resp[1] & (1 << 31)) ? "yes" : "no"); (resp[1] & (1 << 31)) ? "yes" : "no");
afg->wid_start = resp[2] >> 16; afg->wid_start = resp[2] >> 16;
@@ -133,20 +173,24 @@ hda_codec_parse_afg(hda_afg* afg)
} }
/* Iterate over all Widgets and collect info */ /* Iterate over all Widgets and collect info */
for (widx=0; widx < afg->wid_count; widx++) { for (widx = 0; widx < afg->wid_count; widx++) {
uint32 wid = afg->wid_start + widx; uint32 wid = afg->wid_start + widx;
char buf[256]; char buf[256];
int off; int off;
verbs[0] = MAKE_VERB(afg->codec->addr,wid,VID_GET_PARAM,PID_AUDIO_WIDGET_CAP); verbs[0] = MAKE_VERB(afg->codec->addr, wid, VID_GET_PARAM,
verbs[1] = MAKE_VERB(afg->codec->addr,wid,VID_GET_PARAM,PID_CONNLIST_LEN); 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); hda_send_verbs(afg->codec, verbs, resp, 2);
afg->widgets[widx].type = resp[0] >> 20; afg->widgets[widx].type = resp[0] >> 20;
afg->widgets[widx].num_inputs = resp[1] & 0x7F; afg->widgets[widx].num_inputs = resp[1] & 0x7F;
off = 0; off = 0;
if (resp[0] & (1 << 11)) off += sprintf(buf+off, "[L-R Swap] "); if (resp[0] & (1 << 11))
off += sprintf(buf + off, "[L-R Swap] ");
if (resp[0] & (1 << 10)) { if (resp[0] & (1 << 10)) {
corb_t verb; corb_t verb;
uint32 resp; uint32 resp;
@@ -154,21 +198,30 @@ hda_codec_parse_afg(hda_afg* afg)
off += sprintf(buf+off, "[Power] "); off += sprintf(buf+off, "[Power] ");
/* We support power; switch us on! */ /* We support power; switch us on! */
verb = MAKE_VERB(afg->codec->addr,wid,VID_SET_POWERSTATE,0); verb = MAKE_VERB(afg->codec->addr, wid, VID_SET_POWERSTATE, 0);
hda_send_verbs(afg->codec, &verb, &resp, 1); hda_send_verbs(afg->codec, &verb, &resp, 1);
} }
if (resp[0] & (1 << 9)) off += sprintf(buf+off, "[Digital] "); if (resp[0] & (1 << 9))
if (resp[0] & (1 << 7)) off += sprintf(buf+off, "[Unsol Capable] "); off += sprintf(buf + off, "[Digital] ");
if (resp[0] & (1 << 6)) off += sprintf(buf+off, "[Proc Widget] "); if (resp[0] & (1 << 7))
if (resp[0] & (1 << 5)) off += sprintf(buf+off, "[Stripe] "); off += sprintf(buf + off, "[Unsol Capable] ");
if (resp[0] & (1 << 4)) off += sprintf(buf+off, "[Format Override] "); if (resp[0] & (1 << 6))
if (resp[0] & (1 << 3)) off += sprintf(buf+off, "[Amp Param Override] "); off += sprintf(buf + off, "[Proc Widget] ");
if (resp[0] & (1 << 2)) off += sprintf(buf+off, "[Out Amp] "); if (resp[0] & (1 << 5))
if (resp[0] & (1 << 1)) off += sprintf(buf+off, "[In Amp] "); off += sprintf(buf + off, "[Stripe] ");
if (resp[0] & (1 << 0)) off += sprintf(buf+off, "[Stereo] "); if (resp[0] & (1 << 4))
off += sprintf(buf + off, "[Format Override] ");
if (resp[0] & (1 << 3))
off += sprintf(buf + off, "[Amp Param Override] ");
if (resp[0] & (1 << 2))
off += sprintf(buf + off, "[Out Amp] ");
if (resp[0] & (1 << 1))
off += sprintf(buf + off, "[In Amp] ");
if (resp[0] & (1 << 0))
off += sprintf(buf + off, "[Stereo] ");
switch(afg->widgets[widx].type) { switch (afg->widgets[widx].type) {
case WT_AUDIO_OUTPUT: case WT_AUDIO_OUTPUT:
dprintf("%ld:\tAudio Output\n", wid); dprintf("%ld:\tAudio Output\n", wid);
hda_widget_get_stream_support(afg->codec, wid, hda_widget_get_stream_support(afg->codec, wid,
@@ -193,11 +246,12 @@ hda_codec_parse_afg(hda_afg* afg)
break; break;
case WT_PIN_COMPLEX: case WT_PIN_COMPLEX:
dprintf("%ld:\tPin Complex\n", wid); dprintf("%ld:\tPin Complex\n", wid);
verbs[0] = MAKE_VERB(afg->codec->addr,wid,VID_GET_PARAM,PID_PIN_CAP); 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) { 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", dprintf("\t%s%s\n",
afg->widgets[widx].d.pin.input ? "[Input] " : "", afg->widgets[widx].d.pin.input ? "[Input] " : "",
afg->widgets[widx].d.pin.input ? "[Output]" : ""); afg->widgets[widx].d.pin.input ? "[Output]" : "");
@@ -205,16 +259,17 @@ hda_codec_parse_afg(hda_afg* afg)
dprintf("%s: Error getting Pin Complex IO\n", __func__); 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);
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.device = (resp[0] >> 20) & 0xF; afg->widgets[widx].d.pin.device = (resp[0] >> 20) & 0xF;
dprintf("\t%s, %s, %s, %s\n", dprintf("\t%s, %s, %s, %s\n",
portcon[resp[0] >> 30], kPortConnector[resp[0] >> 30],
defdev[afg->widgets[widx].d.pin.device], kDefaultDevice[afg->widgets[widx].d.pin.device],
conntype[(resp[0] >> 16) & 0xF], kConnectionType[(resp[0] >> 16) & 0xF],
jcolor[(resp[0] >> 12) & 0xF]); kJackColor[(resp[0] >> 12) & 0xF]);
} }
hda_widget_get_amplifier_capabilities(afg->codec, wid); hda_widget_get_amplifier_capabilities(afg->codec, wid);
break; break;
case WT_POWER: case WT_POWER:
@@ -236,61 +291,66 @@ hda_codec_parse_afg(hda_afg* afg)
dprintf("\t%s\n", buf); dprintf("\t%s\n", buf);
hda_widget_get_pm_support(afg->codec, wid, &afg->widgets[widx].pm); hda_widget_get_pm_support(afg->codec, wid, &afg->widgets[widx].pm);
if (afg->widgets[widx].num_inputs) { if (afg->widgets[widx].num_inputs) {
int idx; int idx;
off = 0; off = 0;
if (afg->widgets[widx].num_inputs > 1) { if (afg->widgets[widx].num_inputs > 1) {
verbs[0] = MAKE_VERB(afg->codec->addr,wid,VID_GET_CONNSEL,0); verbs[0] = MAKE_VERB(afg->codec->addr, wid, VID_GET_CONNSEL,
0);
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].active_input = resp[0] & 0xFF; afg->widgets[widx].active_input = resp[0] & 0xFF;
else else
afg->widgets[widx].active_input = -1; afg->widgets[widx].active_input = -1;
} else } else
afg->widgets[widx].active_input = -1; afg->widgets[widx].active_input = -1;
for (idx=0; idx < afg->widgets[widx].num_inputs; idx ++) { for (idx = 0; idx < afg->widgets[widx].num_inputs; idx ++) {
if (!(idx % 4)) { if (!(idx % 4)) {
verbs[0] = MAKE_VERB(afg->codec->addr,wid,VID_GET_CONNLENTRY,idx); verbs[0] = MAKE_VERB(afg->codec->addr, wid,
VID_GET_CONNLENTRY, idx);
if (hda_send_verbs(afg->codec, verbs, resp, 1) != B_OK) { if (hda_send_verbs(afg->codec, verbs, resp, 1) != B_OK) {
dprintf("%s: Error parsing inputs for widget %ld!\n", __func__, wid); dprintf("%s: Error parsing inputs for widget %ld!\n",
__func__, wid);
break; break;
} }
} }
if (idx != afg->widgets[widx].active_input) if (idx != afg->widgets[widx].active_input) {
off += sprintf(buf+off, "%ld ", (resp[0] >> (8*(idx%4))) & 0xFF); off += sprintf(buf + off, "%ld ",
else (resp[0] >> (8*(idx%4))) & 0xFF);
off += sprintf(buf+off, "(%ld) ", (resp[0] >> (8*(idx%4))) & 0xFF); } else {
off += sprintf(buf + off, "(%ld) ",
(resp[0] >> (8*(idx%4))) & 0xFF);
}
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); dprintf("\t[ %s]\n", buf);
} }
} }
} }
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.
* Find path from 'wid' to a widget of type 'wtype', returning its widget id.
* Returns 0 if not found. * Returns 0 if not found.
*/ */
static uint32 static uint32
hda_codec_afg_find_path(hda_afg* afg, uint32 wid, uint32 wtype, 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_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_path(afg, afg->widgets[widx].inputs[idx], wtype, 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;
@@ -301,16 +361,16 @@ hda_codec_afg_find_path(hda_afg* afg, uint32 wid, uint32 wtype, uint32 depth)
break; break;
case WT_AUDIO_SELECTOR: case WT_AUDIO_SELECTOR:
{ {
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_path(afg, wid, wtype, depth +1)) { if (hda_codec_afg_find_path(afg, wid, wtype, depth + 1)) {
return wid; return wid;
}
} }
} }
break; break;
}
default: default:
if (afg->widgets[widx].type == wtype) if (afg->widgets[widx].type == wtype)
@@ -322,6 +382,7 @@ hda_codec_afg_find_path(hda_afg* afg, uint32 wid, uint32 wtype, uint32 depth)
return 0; return 0;
} }
static void static void
hda_afg_delete(hda_afg* afg) hda_afg_delete(hda_afg* afg)
{ {
@@ -339,6 +400,7 @@ hda_afg_delete(hda_afg* afg)
} }
} }
static status_t static status_t
hda_codec_afg_new(hda_codec* codec, uint32 afg_nid) hda_codec_afg_new(hda_codec* codec, uint32 afg_nid)
{ {
@@ -346,7 +408,7 @@ hda_codec_afg_new(hda_codec* codec, uint32 afg_nid)
status_t rc; status_t rc;
uint32 idx; uint32 idx;
if ((afg=calloc(1, sizeof(hda_afg))) == NULL) { if ((afg = calloc(1, sizeof(hda_afg))) == NULL) {
rc = B_NO_MEMORY; rc = B_NO_MEMORY;
goto done; goto done;
} }
@@ -360,94 +422,110 @@ hda_codec_afg_new(hda_codec* codec, uint32 afg_nid)
if (rc != B_OK) if (rc != B_OK)
goto free_afg; goto free_afg;
/* Setup for worst-case scenario; /* Setup for worst-case scenario; we cannot find any output Pin Widgets */
we cannot find any output Pin Widgets */
rc = ENODEV; rc = ENODEV;
/* Try to locate all input/output channels */ /* Try to locate all input/output channels */
for (idx=0; idx < afg->wid_count; idx++) { for (idx = 0; idx < afg->wid_count; idx++) {
uint32 output_wid = 0, input_wid = 0; uint32 output_wid = 0, input_wid = 0;
int32 iidx; int32 iidx;
if (afg->playback_stream == NULL && afg->widgets[idx].type == WT_PIN_COMPLEX && afg->widgets[idx].d.pin.output) { if (afg->playback_stream == NULL
if (afg->widgets[idx].d.pin.device == PIN_DEV_HP_OUT || && afg->widgets[idx].type == WT_PIN_COMPLEX
afg->widgets[idx].d.pin.device == PIN_DEV_SPEAKER || && afg->widgets[idx].d.pin.output) {
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
iidx = afg->widgets[idx].active_input; || afg->widgets[idx].d.pin.device == PIN_DEV_LINE_OUT) {
if (iidx != -1) { iidx = afg->widgets[idx].active_input;
output_wid = hda_codec_afg_find_path(afg, afg->widgets[idx].inputs[iidx], WT_AUDIO_OUTPUT, 0); if (iidx != -1) {
} else { output_wid = hda_codec_afg_find_path(afg,
for (iidx=0; iidx < afg->widgets[idx].num_inputs; iidx++) { afg->widgets[idx].inputs[iidx], WT_AUDIO_OUTPUT, 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_path(afg,
if (hda_send_verbs(codec, &verb, NULL, 1) != B_OK) afg->widgets[idx].inputs[iidx], WT_AUDIO_OUTPUT, 0);
dprintf("%s: Setting output selector failed!\n", __func__); if (output_wid) {
break; 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;
}
} }
} }
}
if (output_wid) {
if (!afg->playback_stream) {
corb_t verb[2];
/* 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; enable hp/output */
verb[0] = MAKE_VERB(codec->addr, afg->playback_stream->pin_wid,
VID_SET_AMPGAINMUTE, (1 << 15) | (1 << 13) | (1 << 12));
verb[1] = MAKE_VERB(codec->addr, afg->playback_stream->pin_wid,
VID_SET_PINWCTRL, (1 << 7) | (1 << 6));
hda_send_verbs(codec, verb, NULL, 2);
dprintf("%s: Found output PIN (%s) connected to output CONV wid:%ld\n", if (output_wid) {
__func__, defdev[afg->widgets[idx].d.pin.device], output_wid); if (!afg->playback_stream) {
} corb_t verb[2];
/* Setup playback/record streams for Multi Audio API */
afg->playback_stream = hda_stream_new(
afg->codec->controller, STRM_PLAYBACK);
afg->record_stream = hda_stream_new(
afg->codec->controller, STRM_RECORD);
afg->playback_stream->pin_wid = idx + afg->wid_start;
afg->playback_stream->io_wid = output_wid;
/* FIXME: Force Pin Widget to unmute; enable hp/output */
verb[0] = MAKE_VERB(codec->addr,
afg->playback_stream->pin_wid, VID_SET_AMPGAINMUTE,
(1 << 15) | (1 << 13) | (1 << 12));
verb[1] = MAKE_VERB(codec->addr,
afg->playback_stream->pin_wid, VID_SET_PINWCTRL,
(1 << 7) | (1 << 6));
hda_send_verbs(codec, verb, NULL, 2);
dprintf("%s: Found output PIN (%s) connected to output "
"CONV wid:%ld\n", __func__,
kDefaultDevice[afg->widgets[idx].d.pin.device], output_wid);
}
}
} }
}
} }
if (afg->widgets[idx].type == WT_AUDIO_INPUT) { if (afg->widgets[idx].type == WT_AUDIO_INPUT) {
iidx = afg->widgets[idx].active_input; iidx = afg->widgets[idx].active_input;
if (iidx != -1) { if (iidx != -1) {
input_wid = hda_codec_afg_find_path(afg, afg->widgets[idx].inputs[iidx], WT_PIN_COMPLEX, 0); input_wid = hda_codec_afg_find_path(afg,
afg->widgets[idx].inputs[iidx], WT_PIN_COMPLEX, 0);
} else { } else {
for (iidx=0; iidx < afg->widgets[idx].num_inputs; iidx++) { 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); input_wid = hda_codec_afg_find_path(afg,
afg->widgets[idx].inputs[iidx], WT_PIN_COMPLEX, 0);
if (input_wid) { if (input_wid) {
corb_t verb = MAKE_VERB(codec->addr,idx+afg->wid_start,VID_SET_CONNSEL,iidx); corb_t verb = MAKE_VERB(codec->addr,
if (hda_send_verbs(codec, &verb, NULL, 1) != B_OK) idx + afg->wid_start, VID_SET_CONNSEL, iidx);
dprintf("%s: Setting input selector failed!\n", __func__); if (hda_send_verbs(codec, &verb, NULL, 1) != B_OK) {
dprintf("%s: Setting input selector failed!\n",
__func__);
}
break; break;
} }
} }
} }
if (input_wid) { if (input_wid) {
if (!afg->record_stream) { if (!afg->record_stream) {
corb_t verb; corb_t verb;
/* Setup playback/record streams for Multi Audio API */ /* Setup playback/record streams for Multi Audio API */
afg->record_stream = hda_stream_new(afg->codec->ctrlr, STRM_RECORD); afg->record_stream = hda_stream_new(afg->codec->controller,
STRM_RECORD);
afg->record_stream->pin_wid = input_wid; afg->record_stream->pin_wid = input_wid;
afg->record_stream->io_wid = idx + afg->wid_start; afg->record_stream->io_wid = idx + afg->wid_start;
/* FIXME: Force Pin Widget to unmute */ /* FIXME: Force Pin Widget to unmute */
verb = MAKE_VERB(codec->addr, afg->record_stream->pin_wid, verb = MAKE_VERB(codec->addr, afg->record_stream->pin_wid,
VID_SET_AMPGAINMUTE, (1 << 15) | (1 << 13) | (1 << 12)); VID_SET_AMPGAINMUTE, (1 << 15) | (1 << 13) | (1 << 12));
hda_send_verbs(codec, &verb, NULL, 1); hda_send_verbs(codec, &verb, NULL, 1);
} }
dprintf("%s: Found input PIN (%s) connected to input CONV wid:%ld\n", dprintf("%s: Found input PIN (%s) connected to input CONV "
__func__, defdev[afg->widgets[input_wid-afg->wid_start].d.pin.device], idx+afg->wid_start); "wid:%ld\n", __func__, kDefaultDevice[afg->widgets[
input_wid-afg->wid_start].d.pin.device],
idx + afg->wid_start);
} }
} }
} }
@@ -466,6 +544,7 @@ done:
return rc; return rc;
} }
void void
hda_codec_delete(hda_codec* codec) hda_codec_delete(hda_codec* codec)
{ {
@@ -474,7 +553,7 @@ hda_codec_delete(hda_codec* codec)
delete_sem(codec->response_sem); delete_sem(codec->response_sem);
for (idx=0; idx < codec->num_afgs; idx++) { for (idx = 0; idx < codec->num_afgs; idx++) {
hda_afg_delete(codec->afgs[idx]); hda_afg_delete(codec->afgs[idx]);
codec->afgs[idx] = NULL; codec->afgs[idx] = NULL;
} }
@@ -483,44 +562,45 @@ hda_codec_delete(hda_codec* codec)
} }
} }
hda_codec* hda_codec*
hda_codec_new(hda_controller* ctrlr, uint32 cad) hda_codec_new(hda_controller* controller, uint32 cad)
{ {
hda_codec* codec = calloc(1, sizeof(hda_codec)); hda_codec* codec = calloc(1, sizeof(hda_codec));
uint32 responses[3]; uint32 responses[3];
corb_t verbs[3]; corb_t verbs[3];
status_t rc;
uint32 nid; uint32 nid;
if (codec == NULL) goto exit_new; if (codec == NULL)
goto exit_new;
codec->ctrlr = ctrlr; codec->controller = controller;
codec->addr = cad; codec->addr = cad;
codec->response_sem = create_sem(0, "hda_codec_response_sem"); codec->response_sem = create_sem(0, "hda_codec_response_sem");
ctrlr->codecs[cad] = codec; controller->codecs[cad] = codec;
verbs[0] = MAKE_VERB(cad,0,VID_GET_PARAM,PID_VENDORID); verbs[0] = MAKE_VERB(cad, 0, VID_GET_PARAM, PID_VENDORID);
verbs[1] = MAKE_VERB(cad,0,VID_GET_PARAM,PID_REVISIONID); verbs[1] = MAKE_VERB(cad, 0, VID_GET_PARAM, PID_REVISIONID);
verbs[2] = MAKE_VERB(cad,0,VID_GET_PARAM,PID_SUBORD_NODE_COUNT); verbs[2] = MAKE_VERB(cad, 0, VID_GET_PARAM, PID_SUBORD_NODE_COUNT);
if (hda_send_verbs(codec, verbs, responses, 3) != B_OK) if (hda_send_verbs(codec, verbs, responses, 3) != B_OK)
goto cmd_failed; goto cmd_failed;
dprintf("Codec %ld Vendor: %04lx Product: %04lx\n", dprintf("Codec %ld Vendor: %04lx Product: %04lx\n",
cad, responses[0] >> 16, responses[0] & 0xFFFF); cad, responses[0] >> 16, responses[0] & 0xFFFF);
for (nid=responses[2] >> 16; for (nid = responses[2] >> 16;
nid < (responses[2] >> 16) + (responses[2] & 0xFF); nid < (responses[2] >> 16) + (responses[2] & 0xFF); nid++) {
nid++) {
uint32 resp; uint32 resp;
verbs[0] = MAKE_VERB(cad,nid,VID_GET_PARAM,PID_FUNCGRP_TYPE); verbs[0] = MAKE_VERB(cad, nid, VID_GET_PARAM, PID_FUNCGRP_TYPE);
if ((rc=hda_send_verbs(codec, verbs, &resp, 1)) != B_OK) if (hda_send_verbs(codec, verbs, &resp, 1) != B_OK)
goto cmd_failed; goto cmd_failed;
if ((resp&0xFF) == 1) { if ((resp & 0xFF) == 1) {
/* Found an Audio Function Group! */ /* Found an Audio Function Group! */
if ((rc=hda_codec_afg_new(codec, nid)) != B_OK) { status_t rc = hda_codec_afg_new(codec, nid);
if (rc != B_OK) {
dprintf("%s: Failed to setup new audio function group (%s)!\n", dprintf("%s: Failed to setup new audio function group (%s)!\n",
__func__, strerror(rc)); __func__, strerror(rc));
goto cmd_failed; goto cmd_failed;
@@ -531,7 +611,7 @@ hda_codec_new(hda_controller* ctrlr, uint32 cad)
goto exit_new; goto exit_new;
cmd_failed: cmd_failed:
ctrlr->codecs[cad] = NULL; controller->codecs[cad] = NULL;
hda_codec_delete(codec); hda_codec_delete(codec);
codec = NULL; codec = NULL;
@@ -1,3 +1,10 @@
/*
* Copyright 2007-2008, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ithamar Adema, ithamar AT unet DOT nl
*/
#ifndef HDA_CODEC_H #ifndef HDA_CODEC_H
#define HDA_CODEC_H #define HDA_CODEC_H
@@ -1,117 +1,132 @@
/*
* Copyright 2007-2008, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ithamar Adema, ithamar AT unet DOT nl
*/
#include "driver.h" #include "driver.h"
#include "hda_controller_defs.h" #include "hda_controller_defs.h"
#include "hda_codec_defs.h" #include "hda_codec_defs.h"
#include "driver.h"
void void
hda_stream_delete(hda_stream* s) hda_stream_delete(hda_stream* stream)
{ {
if (s->buffer_ready_sem >= B_OK) if (stream->buffer_ready_sem >= B_OK)
delete_sem(s->buffer_ready_sem); delete_sem(stream->buffer_ready_sem);
if (s->buffer_area >= B_OK)
delete_area(s->buffer_area);
if (s->bdl_area >= B_OK)
delete_area(s->bdl_area);
free(s); if (stream->buffer_area >= B_OK)
delete_area(stream->buffer_area);
if (stream->bdl_area >= B_OK)
delete_area(stream->bdl_area);
free(stream);
} }
hda_stream* hda_stream*
hda_stream_new(hda_controller* ctrlr, int type) hda_stream_new(hda_controller* controller, int type)
{ {
hda_stream* s = calloc(1, sizeof(hda_stream)); hda_stream* stream = calloc(1, sizeof(hda_stream));
if (s != NULL) { if (stream == NULL)
s->buffer_area = B_ERROR; return NULL;
s->bdl_area = B_ERROR;
switch(type) {
case STRM_PLAYBACK:
s->buffer_ready_sem = create_sem(0, "hda_playback_sem");
s->id = 1;
s->off = (ctrlr->num_input_streams * HDAC_SDSIZE);
ctrlr->streams[ctrlr->num_input_streams] = s;
break;
case STRM_RECORD:
s->buffer_area = B_ERROR;
s->bdl_area = B_ERROR;
s->buffer_ready_sem = create_sem(0, "hda_record_sem");
s->id = 2;
s->off = 0;
ctrlr->streams[0] = s;
break;
default: stream->buffer_area = B_ERROR;
dprintf("%s: Unknown stream type %d!\n", __func__, type); stream->bdl_area = B_ERROR;
free(s);
s = NULL; switch (type) {
break; case STRM_PLAYBACK:
} stream->buffer_ready_sem = create_sem(0, "hda_playback_sem");
stream->id = 1;
stream->off = (controller->num_input_streams * HDAC_SDSIZE);
controller->streams[controller->num_input_streams] = stream;
break;
case STRM_RECORD:
stream->buffer_area = B_ERROR;
stream->bdl_area = B_ERROR;
stream->buffer_ready_sem = create_sem(0, "hda_record_sem");
stream->id = 2;
stream->off = 0;
controller->streams[0] = stream;
break;
default:
dprintf("%s: Unknown stream type %d!\n", __func__, type);
free(stream);
stream = NULL;
break;
} }
return s; return stream;
} }
status_t
hda_stream_start(hda_controller* ctrlr, hda_stream* s)
{
OREG8(ctrlr,s->off,CTL0) |= CTL0_RUN;
while (!(OREG8(ctrlr,s->off,CTL0) & CTL0_RUN)) status_t
hda_stream_start(hda_controller* controller, hda_stream* stream)
{
OREG8(controller, stream->off, CTL0) |= CTL0_RUN;
while (!(OREG8(controller, stream->off, CTL0) & CTL0_RUN))
snooze(1); snooze(1);
s->running = true; stream->running = true;
return B_OK; return B_OK;
} }
status_t status_t
hda_stream_check_intr(hda_controller* ctrlr, hda_stream* s) hda_stream_check_intr(hda_controller* controller, hda_stream* stream)
{ {
if (s->running) { if (stream->running) {
uint8 sts = OREG8(ctrlr,s->off,STS); uint8 sts = OREG8(controller, stream->off, STS);
if (sts) { if (sts) {
cpu_status status; cpu_status status;
OREG8(ctrlr,s->off,STS) = sts; OREG8(controller, stream->off, STS) = sts;
status = disable_interrupts(); status = disable_interrupts();
acquire_spinlock(&s->lock); acquire_spinlock(&stream->lock);
s->real_time = system_time(); stream->real_time = system_time();
s->frames_count += s->buffer_length; stream->frames_count += stream->buffer_length;
s->buffer_cycle = (s->buffer_cycle +1) % s->num_buffers; stream->buffer_cycle = (stream->buffer_cycle + 1)
% stream->num_buffers;
release_spinlock(&s->lock); release_spinlock(&stream->lock);
restore_interrupts(status); restore_interrupts(status);
release_sem_etc(s->buffer_ready_sem, 1, B_DO_NOT_RESCHEDULE); release_sem_etc(stream->buffer_ready_sem, 1, B_DO_NOT_RESCHEDULE);
} }
} }
return B_OK; return B_OK;
} }
status_t
hda_stream_stop(hda_controller* ctrlr, hda_stream* s)
{
OREG8(ctrlr,s->off,CTL0) &= ~CTL0_RUN;
while (OREG8(ctrlr,s->off,CTL0) & CTL0_RUN) status_t
hda_stream_stop(hda_controller* controller, hda_stream* stream)
{
OREG8(controller, stream->off, CTL0) &= ~CTL0_RUN;
while ((OREG8(controller, stream->off, CTL0) & CTL0_RUN) != 0)
snooze(1); snooze(1);
s->running = false; stream->running = false;
return B_OK; return B_OK;
} }
status_t status_t
hda_stream_setup_buffers(hda_afg* afg, hda_stream* s, const char* desc) hda_stream_setup_buffers(hda_afg* afg, hda_stream* stream, const char* desc)
{ {
uint32 buffer_size, buffer_pa, alloc; uint32 bufferSize, bufferPhysicalAddress, alloc;
uint32 response[2], idx; uint32 response[2], index;
physical_entry pe; physical_entry pe;
bdl_entry_t* bdl; bdl_entry_t* bdl;
corb_t verb[2]; corb_t verb[2];
@@ -120,237 +135,301 @@ hda_stream_setup_buffers(hda_afg* afg, hda_stream* s, const char* desc)
uint16 wfmt; uint16 wfmt;
/* Clear previously allocated memory */ /* Clear previously allocated memory */
if (s->buffer_area >= B_OK) { if (stream->buffer_area >= B_OK) {
delete_area(s->buffer_area); delete_area(stream->buffer_area);
s->buffer_area = B_ERROR; stream->buffer_area = B_ERROR;
} }
if (s->bdl_area >= B_OK) { if (stream->bdl_area >= B_OK) {
delete_area(s->bdl_area); delete_area(stream->bdl_area);
s->bdl_area = B_ERROR; stream->bdl_area = B_ERROR;
} }
/* Calculate size of buffer (aligned to 128 bytes) */ /* Calculate size of buffer (aligned to 128 bytes) */
buffer_size = s->sample_size * s->num_channels * s->buffer_length; bufferSize = stream->sample_size * stream->num_channels
buffer_size = (buffer_size + 127) & (~127); * stream->buffer_length;
bufferSize = (bufferSize + 127) & (~127);
/* Calculate total size of all buffers (aligned to size of B_PAGE_SIZE) */ /* Calculate total size of all buffers (aligned to size of B_PAGE_SIZE) */
alloc = buffer_size * s->num_buffers; alloc = bufferSize * stream->num_buffers;
alloc = (alloc + B_PAGE_SIZE - 1) & (~(B_PAGE_SIZE -1)); alloc = (alloc + B_PAGE_SIZE - 1) & (~(B_PAGE_SIZE -1));
/* Allocate memory for buffers */ /* Allocate memory for buffers */
s->buffer_area = create_area("hda_buffers", (void**)&buffer, B_ANY_KERNEL_ADDRESS, alloc, B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA); stream->buffer_area = create_area("hda_buffers", (void**)&buffer,
if (s->buffer_area < B_OK) B_ANY_KERNEL_ADDRESS, alloc, B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA);
return s->buffer_area; if (stream->buffer_area < B_OK)
return stream->buffer_area;
/* Get the physical address of memory */ /* Get the physical address of memory */
rc = get_memory_map(buffer, alloc, &pe, 1); rc = get_memory_map(buffer, alloc, &pe, 1);
if (rc != B_OK) { if (rc != B_OK) {
delete_area(s->buffer_area); delete_area(stream->buffer_area);
return rc; return rc;
} }
buffer_pa = (uint32)pe.address; bufferPhysicalAddress = (uint32)pe.address;
dprintf("%s(%s): Allocated %lu bytes for %ld buffers\n", __func__, desc, dprintf("%s(%s): Allocated %lu bytes for %ld buffers\n", __func__, desc,
alloc, s->num_buffers); alloc, stream->num_buffers);
/* Store pointers (both virtual/physical) */ /* Store pointers (both virtual/physical) */
for (idx=0; idx < s->num_buffers; idx++) { for (index = 0; index < stream->num_buffers; index++) {
s->buffers[idx] = buffer + (idx*buffer_size); stream->buffers[index] = buffer + (index * bufferSize);
s->buffers_pa[idx] = buffer_pa + (idx*buffer_size); stream->buffers_pa[index] = bufferPhysicalAddress + (index * bufferSize);
} }
/* Now allocate BDL for buffer range */ /* Now allocate BDL for buffer range */
alloc = s->num_buffers * sizeof(bdl_entry_t); alloc = stream->num_buffers * sizeof(bdl_entry_t);
alloc = (alloc + B_PAGE_SIZE - 1) & (~(B_PAGE_SIZE -1)); alloc = (alloc + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
s->bdl_area = create_area("hda_bdl", (void**)&bdl, B_ANY_KERNEL_ADDRESS, alloc, B_CONTIGUOUS, 0); stream->bdl_area = create_area("hda_bdl", (void**)&bdl,
if (s->bdl_area < B_OK) { B_ANY_KERNEL_ADDRESS, alloc, B_CONTIGUOUS, 0);
delete_area(s->buffer_area); if (stream->bdl_area < B_OK) {
return s->bdl_area; delete_area(stream->buffer_area);
return stream->bdl_area;
} }
/* Get the physical address of memory */ /* Get the physical address of memory */
rc = get_memory_map(bdl, alloc, &pe, 1); rc = get_memory_map(bdl, alloc, &pe, 1);
if (rc != B_OK) { if (rc != B_OK) {
delete_area(s->buffer_area); delete_area(stream->buffer_area);
delete_area(s->bdl_area); delete_area(stream->bdl_area);
return rc; return rc;
} }
s->bdl_pa = (uint32)pe.address; stream->bdl_pa = (uint32)pe.address;
dprintf("%s(%s): Allocated %ld bytes for %ld BDLEs\n", __func__, desc, dprintf("%s(%s): Allocated %ld bytes for %ld BDLEs\n", __func__, desc,
alloc, s->num_buffers); alloc, stream->num_buffers);
/* Setup BDL entries */ /* Setup BDL entries */
for (idx=0; idx < s->num_buffers; idx++, bdl++) { for (index = 0; index < stream->num_buffers; index++, bdl++) {
bdl->address = s->buffers_pa[idx]; bdl->address = stream->buffers_pa[index];
bdl->length = buffer_size; bdl->length = bufferSize;
bdl->ioc = 1; bdl->ioc = 1;
} }
/* Configure stream registers */ /* Configure stream registers */
wfmt = s->num_channels -1; wfmt = stream->num_channels -1;
switch(s->sampleformat) { switch (stream->sampleformat) {
case B_FMT_8BIT_S: wfmt |= (0 << 4); s->bps = 8; break; case B_FMT_8BIT_S: wfmt |= (0 << 4); stream->bps = 8; break;
case B_FMT_16BIT: wfmt |= (1 << 4); s->bps = 16; break; case B_FMT_16BIT: wfmt |= (1 << 4); stream->bps = 16; break;
case B_FMT_24BIT: wfmt |= (3 << 4); s->bps = 24; break; case B_FMT_24BIT: wfmt |= (3 << 4); stream->bps = 24; break;
case B_FMT_32BIT: wfmt |= (4 << 4); s->bps = 32; break; case B_FMT_32BIT: wfmt |= (4 << 4); stream->bps = 32; break;
default: dprintf("%s: Invalid sample format: 0x%lx\n", __func__, s->sampleformat); break;
default:
dprintf("%s: Invalid sample format: 0x%lx\n", __func__,
stream->sampleformat);
break;
} }
switch(s->samplerate) { switch (stream->samplerate) {
case B_SR_8000: wfmt |= (0 << 14) | (0 << 11) | (5 << 8); s->rate=8000; break; case B_SR_8000:
case B_SR_11025: wfmt |= (1 << 14) | (0 << 11) | (3 << 8); s->rate=11025; break; wfmt |= (0 << 14) | (0 << 11) | (5 << 8);
case B_SR_16000: wfmt |= (0 << 14) | (0 << 11) | (2 << 8); s->rate=16000; break; stream->rate = 8000;
case B_SR_22050: wfmt |= (1 << 14) | (0 << 11) | (1 << 8); s->rate=22050; break; break;
case B_SR_32000: wfmt |= (0 << 14) | (1 << 11) | (2 << 8); s->rate=32000; break; case B_SR_11025:
case B_SR_44100: wfmt |= (1 << 14) | (0 << 11) | (0 << 8); s->rate=44100; break; wfmt |= (1 << 14) | (0 << 11) | (3 << 8);
case B_SR_48000: wfmt |= (0 << 14) | (0 << 11) | (0 << 8); s->rate=48000; break; stream->rate = 11025;
case B_SR_88200: wfmt |= (1 << 14) | (1 << 11) | (0 << 8); s->rate=88200; break; break;
case B_SR_96000: wfmt |= (0 << 14) | (2 << 11) | (0 << 8); s->rate=96000; break; case B_SR_16000:
case B_SR_176400: wfmt |= (1 << 14) | (3 << 11) | (0 << 8); s->rate=176400; break; wfmt |= (0 << 14) | (0 << 11) | (2 << 8);
case B_SR_192000: wfmt |= (0 << 14) | (3 << 11) | (0 << 8); s->rate=192000; break; stream->rate = 16000;
default: dprintf("%s: Invalid sample rate: 0x%lx\n", __func__, s->samplerate); break; break;
case B_SR_22050:
wfmt |= (1 << 14) | (0 << 11) | (1 << 8);
stream->rate = 22050;
break;
case B_SR_32000:
wfmt |= (0 << 14) | (1 << 11) | (2 << 8);
stream->rate = 32000;
break;
case B_SR_44100:
wfmt |= (1 << 14) | (0 << 11) | (0 << 8);
stream->rate = 44100;
break;
case B_SR_48000:
wfmt |= (0 << 14) | (0 << 11) | (0 << 8);
stream->rate = 48000;
break;
case B_SR_88200:
wfmt |= (1 << 14) | (1 << 11) | (0 << 8);
stream->rate = 88200;
break;
case B_SR_96000:
wfmt |= (0 << 14) | (2 << 11) | (0 << 8);
stream->rate = 96000;
break;
case B_SR_176400:
wfmt |= (1 << 14) | (3 << 11) | (0 << 8);
stream->rate = 176400;
break;
case B_SR_192000:
wfmt |= (0 << 14) | (3 << 11) | (0 << 8);
stream->rate = 192000;
break;
default:
dprintf("%s: Invalid sample rate: 0x%lx\n", __func__,
stream->samplerate);
break;
} }
dprintf("IRA: %s: setup stream %ld: SR=%ld, SF=%ld\n", __func__, s->id, s->rate, s->bps); dprintf("IRA: %s: setup stream %ld: SR=%ld, SF=%ld\n", __func__, stream->id,
stream->rate, stream->bps);
OREG16(afg->codec->ctrlr,s->off,FMT) = wfmt; OREG16(afg->codec->controller, stream->off, FMT) = wfmt;
OREG32(afg->codec->ctrlr,s->off,BDPL) = s->bdl_pa; OREG32(afg->codec->controller, stream->off, BDPL) = stream->bdl_pa;
OREG32(afg->codec->ctrlr,s->off,BDPU) = 0; OREG32(afg->codec->controller, stream->off, BDPU) = 0;
OREG16(afg->codec->ctrlr,s->off,LVI) = s->num_buffers -1; OREG16(afg->codec->controller, stream->off, LVI) = stream->num_buffers -1;
OREG32(afg->codec->ctrlr,s->off,CBL) = s->sample_size * s->num_channels * s->num_buffers * s->buffer_length; /* total cyclic buffer size in _bytes_ */ /* total cyclic buffer size in _bytes_ */
OREG8(afg->codec->ctrlr,s->off,CTL0) = CTL0_IOCE | CTL0_FEIE | CTL0_DEIE; OREG32(afg->codec->controller, stream->off, CBL) = stream->sample_size
OREG8(afg->codec->ctrlr,s->off,CTL2) = s->id << 4; * stream->num_channels * stream->num_buffers * stream->buffer_length;
OREG8(afg->codec->controller, stream->off, CTL0)
= CTL0_IOCE | CTL0_FEIE | CTL0_DEIE;
OREG8(afg->codec->controller, stream->off, CTL2) = stream->id << 4;
verb[0] = MAKE_VERB(afg->codec->addr, s->io_wid, VID_SET_CONVFORMAT, wfmt); verb[0] = MAKE_VERB(afg->codec->addr, stream->io_wid, VID_SET_CONVFORMAT,
verb[1] = MAKE_VERB(afg->codec->addr, s->io_wid, VID_SET_CVTSTRCHN, s->id << 4); wfmt);
verb[1] = MAKE_VERB(afg->codec->addr, stream->io_wid, VID_SET_CVTSTRCHN,
stream->id << 4);
rc = hda_send_verbs(afg->codec, verb, response, 2); rc = hda_send_verbs(afg->codec, verb, response, 2);
return rc; return rc;
} }
//#pragma mark -
// #pragma mark -
status_t status_t
hda_send_verbs(hda_codec* codec, corb_t* verbs, uint32* responses, int count) hda_send_verbs(hda_codec* codec, corb_t* verbs, uint32* responses, int count)
{ {
corb_t* corb = codec->ctrlr->corb; corb_t* corb = codec->controller->corb;
status_t rc; status_t rc;
codec->response_count = 0; codec->response_count = 0;
memcpy(corb+(codec->ctrlr->corbwp +1), verbs, sizeof(corb_t)*count); memcpy(corb + (codec->controller->corbwp + 1), verbs,
REG16(codec->ctrlr,CORBWP) = (codec->ctrlr->corbwp += count); sizeof(corb_t) * count);
REG16(codec->controller, CORBWP) = (codec->controller->corbwp += count);
rc = acquire_sem_etc(codec->response_sem, count, B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT, 1000ULL * 50); rc = acquire_sem_etc(codec->response_sem, count, /*B_CAN_INTERRUPT | */
B_RELATIVE_TIMEOUT, 1000ULL * 50);
if (rc == B_OK && responses != NULL) if (rc == B_OK && responses != NULL)
memcpy(responses, codec->responses, count*sizeof(uint32)); memcpy(responses, codec->responses, count * sizeof(uint32));
return rc; return rc;
} }
static int32 static int32
hda_interrupt_handler(hda_controller* ctrlr) hda_interrupt_handler(hda_controller* controller)
{ {
int32 rc = B_UNHANDLED_INTERRUPT; int32 rc = B_HANDLED_INTERRUPT;
/* Check if this interrupt is ours */ /* Check if this interrupt is ours */
uint32 intsts = REG32(ctrlr, INTSTS); uint32 intsts = REG32(controller, INTSTS);
if (intsts & INTSTS_GIS) { if ((intsts & INTSTS_GIS) == 0)
rc = B_HANDLED_INTERRUPT; return B_UNHANDLED_INTERRUPT;
/* Controller or stream related? */ /* Controller or stream related? */
if (intsts & INTSTS_CIS) { if (intsts & INTSTS_CIS) {
uint32 statests = REG16(ctrlr,STATESTS); uint32 statests = REG16(controller, STATESTS);
uint8 rirbsts = REG8(ctrlr,RIRBSTS); uint8 rirbsts = REG8(controller, RIRBSTS);
uint8 corbsts = REG8(ctrlr,CORBSTS); uint8 corbsts = REG8(controller, CORBSTS);
if (statests) { if (statests) {
/* Detected Codec state change */ /* Detected Codec state change */
REG16(ctrlr,STATESTS) = statests; REG16(controller, STATESTS) = statests;
ctrlr->codecsts = statests; controller->codecsts = statests;
}
/* Check for incoming responses */
if (rirbsts) {
REG8(ctrlr,RIRBSTS) = rirbsts;
if (rirbsts & RIRBSTS_RINTFL) {
uint16 rirbwp = REG16(ctrlr,RIRBWP);
while (ctrlr->rirbrp <= rirbwp) {
uint32 resp_ex = ctrlr->rirb[ctrlr->rirbrp].resp_ex;
uint32 cad = resp_ex & HDA_MAXCODECS;
hda_codec* codec = ctrlr->codecs[cad];
if (resp_ex & RESP_EX_UNSOL) {
dprintf("%s: Usolicited response: %08lx/%08lx\n", __func__,
ctrlr->rirb[ctrlr->rirbrp].response, resp_ex);
} else if (codec) {
/* Store responses in codec */
codec->responses[codec->response_count++] = ctrlr->rirb[ctrlr->rirbrp].response;
release_sem_etc(codec->response_sem, 1, B_DO_NOT_RESCHEDULE);
rc = B_INVOKE_SCHEDULER;
} else {
dprintf("%s: Response for unknown codec %ld: %08lx/%08lx\n", __func__, cad,
ctrlr->rirb[ctrlr->rirbrp].response, resp_ex);
}
++ctrlr->rirbrp;
}
}
if (rirbsts & RIRBSTS_OIS)
dprintf("%s: RIRB Overflow\n", __func__);
}
/* Check for sending errors */
if (corbsts) {
REG8(ctrlr,CORBSTS) = corbsts;
if (corbsts & CORBSTS_MEI)
dprintf("%s: CORB Memory Error!\n", __func__);
}
} }
if (intsts & ~(INTSTS_CIS|INTSTS_GIS)) { /* Check for incoming responses */
int idx; if (rirbsts) {
for (idx=0; idx < HDA_MAXSTREAMS; idx++) { REG8(controller, RIRBSTS) = rirbsts;
if (intsts & (1 << idx)) {
if (ctrlr->streams[idx]) if (rirbsts & RIRBSTS_RINTFL) {
hda_stream_check_intr(ctrlr, ctrlr->streams[idx]); uint16 rirbwp = REG16(controller, RIRBWP);
else while (controller->rirbrp <= rirbwp) {
dprintf("%s: Stream interrupt for unconfigured stream %d!\n", __func__, idx); uint32 resp_ex
} = controller->rirb[controller->rirbrp].resp_ex;
uint32 cad = resp_ex & HDA_MAXCODECS;
hda_codec* codec = controller->codecs[cad];
if (resp_ex & RESP_EX_UNSOL) {
dprintf("%s: Unsolicited response: %08lx/%08lx\n",
__func__,
controller->rirb[controller->rirbrp].response,
resp_ex);
} else if (codec) {
/* Store responses in codec */
codec->responses[codec->response_count++]
= controller->rirb[controller->rirbrp].response;
release_sem_etc(codec->response_sem, 1,
B_DO_NOT_RESCHEDULE);
rc = B_INVOKE_SCHEDULER;
} else {
dprintf("%s: Response for unknown codec %ld: "
"%08lx/%08lx\n", __func__, cad,
controller->rirb[controller->rirbrp].response,
resp_ex);
}
++controller->rirbrp;
}
} }
if (rirbsts & RIRBSTS_OIS)
dprintf("%s: RIRB Overflow\n", __func__);
}
/* Check for sending errors */
if (corbsts) {
REG8(controller, CORBSTS) = corbsts;
if (corbsts & CORBSTS_MEI)
dprintf("%s: CORB Memory Error!\n", __func__);
} }
/* NOTE: See HDA001 => CIS/GIS cannot be cleared! */
} }
if (intsts & ~(INTSTS_CIS | INTSTS_GIS)) {
int index;
for (index = 0; index < HDA_MAXSTREAMS; index++) {
if ((intsts & (1 << index)) != 0) {
if (controller->streams[index])
hda_stream_check_intr(controller, controller->streams[index]);
else {
dprintf("%s: Stream interrupt for unconfigured stream "
"%d!\n", __func__, index);
}
}
}
}
/* NOTE: See HDA001 => CIS/GIS cannot be cleared! */
return rc; return rc;
} }
static status_t static status_t
hda_hw_start(hda_controller* ctrlr) hda_hw_start(hda_controller* controller)
{ {
int timeout = 10; int timeout = 10;
/* Put controller out of reset mode */ /* Put controller out of reset mode */
REG32(ctrlr,GCTL) |= GCTL_CRST; REG32(controller, GCTL) |= GCTL_CRST;
do { do {
snooze(100); snooze(100);
} while (--timeout && !(REG32(ctrlr,GCTL) & GCTL_CRST)); } while (--timeout && !(REG32(controller, GCTL) & GCTL_CRST));
return timeout ? B_OK : B_TIMED_OUT; return timeout ? B_OK : B_TIMED_OUT;
} }
static status_t static status_t
hda_hw_corb_rirb_init(hda_controller* ctrlr) hda_hw_corb_rirb_init(hda_controller* controller)
{ {
uint32 memsz, rirboff; uint32 memsz, rirboff;
uint8 corbsz, rirbsz; uint8 corbsz, rirbsz;
@@ -358,174 +437,171 @@ hda_hw_corb_rirb_init(hda_controller* ctrlr)
physical_entry pe; physical_entry pe;
/* Determine and set size of CORB */ /* Determine and set size of CORB */
corbsz = REG8(ctrlr,CORBSIZE); corbsz = REG8(controller, CORBSIZE);
if (corbsz & CORBSIZE_CAP_256E) { if (corbsz & CORBSIZE_CAP_256E) {
ctrlr->corblen = 256; controller->corblen = 256;
REG8(ctrlr,CORBSIZE) = CORBSIZE_SZ_256E; REG8(controller, CORBSIZE) = CORBSIZE_SZ_256E;
} else if (corbsz & CORBSIZE_CAP_16E) { } else if (corbsz & CORBSIZE_CAP_16E) {
ctrlr->corblen = 16; controller->corblen = 16;
REG8(ctrlr,CORBSIZE) = CORBSIZE_SZ_16E; REG8(controller, CORBSIZE) = CORBSIZE_SZ_16E;
} else if (corbsz & CORBSIZE_CAP_2E) { } else if (corbsz & CORBSIZE_CAP_2E) {
ctrlr->corblen = 2; controller->corblen = 2;
REG8(ctrlr,CORBSIZE) = CORBSIZE_SZ_2E; REG8(controller, CORBSIZE) = CORBSIZE_SZ_2E;
} }
/* Determine and set size of RIRB */ /* Determine and set size of RIRB */
rirbsz = REG8(ctrlr,RIRBSIZE); rirbsz = REG8(controller, RIRBSIZE);
if (rirbsz & RIRBSIZE_CAP_256E) { if (rirbsz & RIRBSIZE_CAP_256E) {
ctrlr->rirblen = 256; controller->rirblen = 256;
REG8(ctrlr,RIRBSIZE) = RIRBSIZE_SZ_256E; REG8(controller, RIRBSIZE) = RIRBSIZE_SZ_256E;
} else if (rirbsz & RIRBSIZE_CAP_16E) { } else if (rirbsz & RIRBSIZE_CAP_16E) {
ctrlr->rirblen = 16; controller->rirblen = 16;
REG8(ctrlr,RIRBSIZE) = RIRBSIZE_SZ_16E; REG8(controller, RIRBSIZE) = RIRBSIZE_SZ_16E;
} else if (rirbsz & RIRBSIZE_CAP_2E) { } else if (rirbsz & RIRBSIZE_CAP_2E) {
ctrlr->rirblen = 2; controller->rirblen = 2;
REG8(ctrlr,RIRBSIZE) = RIRBSIZE_SZ_2E; REG8(controller, RIRBSIZE) = RIRBSIZE_SZ_2E;
} }
/* Determine rirb offset in memory and total size of corb+alignment+rirb */ /* Determine rirb offset in memory and total size of corb+alignment+rirb */
rirboff = (ctrlr->corblen * sizeof(corb_t) + 0x7f) & ~0x7f; rirboff = (controller->corblen * sizeof(corb_t) + 0x7f) & ~0x7f;
memsz = ((B_PAGE_SIZE -1) + memsz = (rirboff + controller->rirblen * sizeof(rirb_t) + B_PAGE_SIZE - 1)
rirboff + & ~(B_PAGE_SIZE - 1);
(ctrlr->rirblen * sizeof(rirb_t))) & ~(B_PAGE_SIZE-1);
/* Allocate memory area */ /* Allocate memory area */
ctrlr->rb_area = create_area("hda_corb_rirb", controller->rb_area = create_area("hda_corb_rirb", (void**)&controller->corb,
(void**)&ctrlr->corb, B_ANY_KERNEL_ADDRESS, memsz, B_CONTIGUOUS, 0); B_ANY_KERNEL_ADDRESS, memsz, B_CONTIGUOUS, 0);
if (ctrlr->rb_area < 0) { if (controller->rb_area < 0)
return ctrlr->rb_area; return controller->rb_area;
}
/* Rirb is after corb+aligment */ /* Rirb is after corb+aligment */
ctrlr->rirb = (rirb_t*)(((uint8*)ctrlr->corb)+rirboff); controller->rirb = (rirb_t*)(((uint8*)controller->corb) + rirboff);
if ((rc=get_memory_map(ctrlr->corb, memsz, &pe, 1)) != B_OK) { if ((rc = get_memory_map(controller->corb, memsz, &pe, 1)) != B_OK) {
delete_area(ctrlr->rb_area); delete_area(controller->rb_area);
return rc; return rc;
} }
/* Program CORB/RIRB for these locations */ /* Program CORB/RIRB for these locations */
REG32(ctrlr,CORBLBASE) = (uint32)pe.address; REG32(controller, CORBLBASE) = (uint32)pe.address;
REG32(ctrlr,RIRBLBASE) = (uint32)pe.address + rirboff; REG32(controller, RIRBLBASE) = (uint32)pe.address + rirboff;
/* Reset CORB read pointer */ /* Reset CORB read pointer */
/* NOTE: See HDA011 for corrected procedure! */ /* NOTE: See HDA011 for corrected procedure! */
REG16(ctrlr,CORBRP) = CORBRP_RST; REG16(controller, CORBRP) = CORBRP_RST;
do { do {
snooze(10); snooze(10);
} while ( !(REG16(ctrlr,CORBRP) & CORBRP_RST) ); } while ( !(REG16(controller, CORBRP) & CORBRP_RST) );
REG16(ctrlr,CORBRP) = 0; REG16(controller, CORBRP) = 0;
/* Reset RIRB write pointer */ /* Reset RIRB write pointer */
REG16(ctrlr,RIRBWP) = RIRBWP_RST; REG16(controller, RIRBWP) = RIRBWP_RST;
/* Generate interrupt for every response */ /* Generate interrupt for every response */
REG16(ctrlr,RINTCNT) = 1; REG16(controller, RINTCNT) = 1;
/* Setup cached read/write indices */ /* Setup cached read/write indices */
ctrlr->rirbrp = 1; controller->rirbrp = 1;
ctrlr->corbwp = 0; controller->corbwp = 0;
/* Gentlemen, start your engines... */ /* Gentlemen, start your engines... */
REG8(ctrlr,CORBCTL) = CORBCTL_RUN | CORBCTL_MEIE; REG8(controller, CORBCTL) = CORBCTL_RUN | CORBCTL_MEIE;
REG8(ctrlr,RIRBCTL) = RIRBCTL_DMAEN | RIRBCTL_OIC | RIRBCTL_RINTCTL; REG8(controller, RIRBCTL) = RIRBCTL_DMAEN | RIRBCTL_OIC | RIRBCTL_RINTCTL;
return B_OK; return B_OK;
} }
// #pragma mark -
//#pragma mark -
/* Setup hardware for use; detect codecs; etc */ /*! Setup hardware for use; detect codecs; etc */
status_t status_t
hda_hw_init(hda_controller* ctrlr) hda_hw_init(hda_controller* controller)
{ {
status_t rc; status_t rc;
uint16 gcap; uint16 gcap;
uint32 idx; uint32 index;
/* Map MMIO registers */ /* Map MMIO registers */
ctrlr->regs_area = controller->regs_area = map_physical_memory("hda_hw_regs",
map_physical_memory("hda_hw_regs", (void*)ctrlr->pcii.u.h0.base_registers[0], (void*)controller->pci_info.u.h0.base_registers[0],
ctrlr->pcii.u.h0.base_register_sizes[0], B_ANY_KERNEL_ADDRESS, 0, controller->pci_info.u.h0.base_register_sizes[0], B_ANY_KERNEL_ADDRESS,
(void**)&ctrlr->regs); 0, (void**)&controller->regs);
if (controller->regs_area < B_OK) {
if (ctrlr->regs_area < B_OK) { rc = controller->regs_area;
rc = ctrlr->regs_area;
goto error; goto error;
} }
/* Absolute minimum hw is online; we can now install interrupt handler */ /* Absolute minimum hw is online; we can now install interrupt handler */
ctrlr->irq = ctrlr->pcii.u.h0.interrupt_line; controller->irq = controller->pci_info.u.h0.interrupt_line;
rc = install_io_interrupt_handler(ctrlr->irq, rc = install_io_interrupt_handler(controller->irq,
(interrupt_handler)hda_interrupt_handler, ctrlr, 0); (interrupt_handler)hda_interrupt_handler, controller, 0);
if (rc != B_OK) if (rc != B_OK)
goto no_irq; goto no_irq;
/* show some hw features */ /* show some hw features */
gcap = REG16(ctrlr,GCAP); gcap = REG16(controller, GCAP);
dprintf("HDA: HDA v%d.%d, O:%d/I:%d/B:%d, #SDO:%d, 64bit:%s\n", dprintf("HDA: HDA v%d.%d, O:%d/I:%d/B:%d, #SDO:%d, 64bit:%s\n",
REG8(ctrlr,VMAJ), REG8(ctrlr,VMIN), REG8(controller, VMAJ), REG8(controller, VMIN),
GCAP_OSS(gcap), GCAP_ISS(gcap), GCAP_BSS(gcap), GCAP_OSS(gcap), GCAP_ISS(gcap), GCAP_BSS(gcap),
GCAP_NSDO(gcap) ? GCAP_NSDO(gcap) *2 : 1, GCAP_NSDO(gcap) ? GCAP_NSDO(gcap) *2 : 1,
gcap & GCAP_64OK ? "yes" : "no" ); gcap & GCAP_64OK ? "yes" : "no" );
ctrlr->num_input_streams = GCAP_OSS(gcap); controller->num_input_streams = GCAP_OSS(gcap);
ctrlr->num_output_streams = GCAP_ISS(gcap); controller->num_output_streams = GCAP_ISS(gcap);
ctrlr->num_bidir_streams = GCAP_BSS(gcap); controller->num_bidir_streams = GCAP_BSS(gcap);
/* Get controller into valid state */ /* Get controller into valid state */
rc = hda_hw_start(ctrlr); rc = hda_hw_start(controller);
if (rc != B_OK) if (rc != B_OK)
goto reset_failed; goto reset_failed;
/* Setup CORB/RIRB */ /* Setup CORB/RIRB */
rc = hda_hw_corb_rirb_init(ctrlr); rc = hda_hw_corb_rirb_init(controller);
if (rc != B_OK) if (rc != B_OK)
goto corb_rirb_failed; goto corb_rirb_failed;
REG16(ctrlr,WAKEEN) = 0x7fff; REG16(controller, WAKEEN) = 0x7fff;
/* Enable controller interrupts */ /* Enable controller interrupts */
REG32(ctrlr,INTCTL) = INTCTL_GIE | INTCTL_CIE | 0xffff; REG32(controller, INTCTL) = INTCTL_GIE | INTCTL_CIE | 0xffff;
/* Wait for codecs to warm up */ /* Wait for codecs to warm up */
snooze(1000); snooze(1000);
if (!ctrlr->codecsts) { if (!controller->codecsts) {
rc = ENODEV; rc = ENODEV;
goto corb_rirb_failed; goto corb_rirb_failed;
} }
for (idx=0; idx < HDA_MAXCODECS; idx++) for (index = 0; index < HDA_MAXCODECS; index++) {
if (ctrlr->codecsts & (1 << idx)) if ((controller->codecsts & (1 << index)) != 0)
hda_codec_new(ctrlr, idx); hda_codec_new(controller, index);
}
for (idx=0; idx < HDA_MAXCODECS; idx++) { for (index = 0; index < HDA_MAXCODECS; index++) {
if (ctrlr->codecs[idx] && ctrlr->codecs[idx]->num_afgs) { if (controller->codecs[index] && controller->codecs[index]->num_afgs) {
ctrlr->active_codec = ctrlr->codecs[idx]; controller->active_codec = controller->codecs[index];
break; break;
} }
} }
if (ctrlr->active_codec != NULL) if (controller->active_codec != NULL)
return B_OK; return B_OK;
else
rc = ENODEV; rc = ENODEV;
corb_rirb_failed: corb_rirb_failed:
REG32(ctrlr,INTCTL) = 0; REG32(controller, INTCTL) = 0;
reset_failed: reset_failed:
remove_io_interrupt_handler(ctrlr->irq, remove_io_interrupt_handler(controller->irq,
(interrupt_handler)hda_interrupt_handler, (interrupt_handler)hda_interrupt_handler, controller);
ctrlr);
no_irq: no_irq:
delete_area(ctrlr->regs_area); delete_area(controller->regs_area);
ctrlr->regs_area = B_ERROR; controller->regs_area = B_ERROR;
ctrlr->regs = NULL; controller->regs = NULL;
error: error:
dprintf("ERROR: %s(%ld)\n", strerror(rc), rc); dprintf("ERROR: %s(%ld)\n", strerror(rc), rc);
@@ -533,57 +609,61 @@ error:
return rc; return rc;
} }
/* Stop any activity */
/*! Stop any activity */
void void
hda_hw_stop(hda_controller* ctrlr) hda_hw_stop(hda_controller* controller)
{ {
int idx; int index;
/* Stop all audio streams */ /* Stop all audio streams */
for (idx=0; idx < HDA_MAXSTREAMS; idx++) for (index = 0; index < HDA_MAXSTREAMS; index++)
if (ctrlr->streams[idx] && ctrlr->streams[idx]->running) if (controller->streams[index] && controller->streams[index]->running)
hda_stream_stop(ctrlr, ctrlr->streams[idx]); hda_stream_stop(controller, controller->streams[index]);
} }
/* Free resources */
/*! Free resources */
void void
hda_hw_uninit(hda_controller* ctrlr) hda_hw_uninit(hda_controller* controller)
{ {
if (ctrlr != NULL) { uint32 index;
uint32 idx;
/* Stop all audio streams */ if (controller == NULL)
hda_hw_stop(ctrlr); return;
/* Stop CORB/RIRB */
REG8(ctrlr,CORBCTL) = 0;
REG8(ctrlr,RIRBCTL) = 0;
/* Disable interrupts and remove interrupt handler */ /* Stop all audio streams */
REG32(ctrlr,INTCTL) = 0; hda_hw_stop(controller);
REG32(ctrlr,GCTL) &= ~GCTL_CRST;
remove_io_interrupt_handler(ctrlr->irq,
(interrupt_handler)hda_interrupt_handler,
ctrlr);
/* Delete corb/rirb area */ /* Stop CORB/RIRB */
if (ctrlr->rb_area >= 0) { REG8(controller, CORBCTL) = 0;
delete_area(ctrlr->rb_area); REG8(controller, RIRBCTL) = 0;
ctrlr->rb_area = B_ERROR;
ctrlr->corb = NULL; /* Disable interrupts and remove interrupt handler */
ctrlr->rirb = NULL; REG32(controller, INTCTL) = 0;
} REG32(controller, GCTL) &= ~GCTL_CRST;
remove_io_interrupt_handler(controller->irq,
/* Unmap registers */ (interrupt_handler)hda_interrupt_handler, controller);
if (ctrlr->regs_area >= 0) {
delete_area(ctrlr->regs_area); /* Delete corb/rirb area */
ctrlr->regs_area = B_ERROR; if (controller->rb_area >= 0) {
ctrlr->regs = NULL; delete_area(controller->rb_area);
} controller->rb_area = B_ERROR;
controller->corb = NULL;
/* Now delete all codecs */ controller->rirb = NULL;
for (idx=0; idx < HDA_MAXCODECS; idx++) }
if (ctrlr->codecs[idx] != NULL)
hda_codec_delete(ctrlr->codecs[idx]); /* Unmap registers */
if (controller->regs_area >= 0) {
delete_area(controller->regs_area);
controller->regs_area = B_ERROR;
controller->regs = NULL;
}
/* Now delete all codecs */
for (index = 0; index < HDA_MAXCODECS; index++) {
if (controller->codecs[index] != NULL)
hda_codec_delete(controller->codecs[index]);
} }
} }
@@ -1,16 +1,26 @@
/*
* Copyright 2007-2008, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ithamar Adema, ithamar AT unet DOT nl
*/
#ifndef HDAC_REGS_H #ifndef HDAC_REGS_H
#define HDAC_REGS_H #define HDAC_REGS_H
#include <SupportDefs.h> #include <SupportDefs.h>
/* Accessors for HDA controller registers */ /* Accessors for HDA controller registers */
#define REG32(ctrlr,reg) (*(vuint32*)((ctrlr)->regs + HDAC_##reg)) #define REG32(controller, reg) (*(vuint32*)((controller)->regs + HDAC_##reg))
#define REG16(ctrlr,reg) (*(vuint16*)((ctrlr)->regs + HDAC_##reg)) #define REG16(controller, reg) (*(vuint16*)((controller)->regs + HDAC_##reg))
#define REG8(ctrlr,reg) (*((ctrlr)->regs + HDAC_##reg)) #define REG8(controller, reg) (*((controller)->regs + HDAC_##reg))
#define OREG32(ctrlr,s,reg) (*(vuint32*)((ctrlr)->regs + HDAC_SDBASE + (s) + HDAC_SD_##reg)) #define OREG32(controller, stream, reg) \
#define OREG16(ctrlr,s,reg) (*(vuint16*)((ctrlr)->regs + HDAC_SDBASE + (s) + HDAC_SD_##reg)) (*(vuint32*)((controller)->regs + HDAC_SDBASE + (stream) + HDAC_SD_##reg))
#define OREG8(ctrlr,s,reg) (*((ctrlr)->regs + HDAC_SDBASE + (s) + HDAC_SD_##reg)) #define OREG16(controller, stream, reg) \
(*(vuint16*)((controller)->regs + HDAC_SDBASE + (stream) + HDAC_SD_##reg))
#define OREG8(controller, stream, reg) \
(*((controller)->regs + HDAC_SDBASE + (stream) + HDAC_SD_##reg))
/* Register definitions */ /* Register definitions */
#define HDAC_GCAP 0x00 /* 16bits */ #define HDAC_GCAP 0x00 /* 16bits */
@@ -1,7 +1,17 @@
/*
* Copyright 2007-2008, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ithamar Adema, ithamar AT unet DOT nl
*/
#include "multi_audio.h" #include "multi_audio.h"
#include "driver.h" #include "driver.h"
multi_channel_info chans[] = {
static multi_channel_info sChannels[] = {
{ 0, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 }, { 0, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 },
{ 1, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 }, { 1, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 },
{ 2, B_MULTI_INPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 }, { 2, B_MULTI_INPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 },
@@ -12,10 +22,11 @@ multi_channel_info chans[] = {
{ 7, B_MULTI_INPUT_BUS, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO }, { 7, B_MULTI_INPUT_BUS, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO },
}; };
static int32 static int32
format2size(uint32 format) format2size(uint32 format)
{ {
switch(format) { switch (format) {
case B_FMT_8BIT_S: case B_FMT_8BIT_S:
case B_FMT_16BIT: case B_FMT_16BIT:
return 2; return 2;
@@ -27,20 +38,21 @@ format2size(uint32 format)
case B_FMT_FLOAT: case B_FMT_FLOAT:
return 8; return 8;
default: default:
return -1; return -1;
} }
} }
static status_t static status_t
get_description(hda_afg* afg, multi_description* data) get_description(hda_afg* afg, multi_description* data)
{ {
data->interface_version = B_CURRENT_INTERFACE_VERSION; data->interface_version = B_CURRENT_INTERFACE_VERSION;
data->interface_minimum = B_CURRENT_INTERFACE_VERSION; data->interface_minimum = B_CURRENT_INTERFACE_VERSION;
strcpy(data->friendly_name,"HD Audio"); strcpy(data->friendly_name, "HD Audio");
strcpy(data->vendor_info,"Haiku"); strcpy(data->vendor_info, "Haiku");
data->output_channel_count = 2; data->output_channel_count = 2;
data->input_channel_count = 2; data->input_channel_count = 2;
@@ -48,10 +60,12 @@ get_description(hda_afg* afg, multi_description* data)
data->input_bus_channel_count = 2; data->input_bus_channel_count = 2;
data->aux_bus_channel_count = 0; data->aux_bus_channel_count = 0;
dprintf("%s: request_channel_count: %ld\n", __func__, data->request_channel_count); dprintf("%s: request_channel_count: %ld\n", __func__,
data->request_channel_count);
if (data->request_channel_count >= (int)(sizeof(chans) / sizeof(chans[0]))) { if (data->request_channel_count >= (int)(sizeof(sChannels)
memcpy(data->channels,&chans,sizeof(chans)); / sizeof(sChannels[0]))) {
memcpy(data->channels, &sChannels, sizeof(sChannels));
} }
/* determine output/input rates */ /* determine output/input rates */
@@ -74,11 +88,12 @@ get_description(hda_afg* afg, multi_description* data)
data->interface_flags = B_MULTI_INTERFACE_PLAYBACK /* | B_MULTI_INTERFACE_RECORD */; data->interface_flags = B_MULTI_INTERFACE_PLAYBACK /* | B_MULTI_INTERFACE_RECORD */;
data->start_latency = 30000; data->start_latency = 30000;
strcpy(data->control_panel,""); strcpy(data->control_panel, "");
return B_OK; return B_OK;
} }
static status_t static status_t
get_enabled_channels(hda_afg* afg, multi_channel_enable* data) get_enabled_channels(hda_afg* afg, multi_channel_enable* data)
{ {
@@ -91,6 +106,7 @@ get_enabled_channels(hda_afg* afg, multi_channel_enable* data)
return B_OK; return B_OK;
} }
static status_t static status_t
get_global_format(hda_afg* afg, multi_format_info* data) get_global_format(hda_afg* afg, multi_format_info* data)
{ {
@@ -107,6 +123,7 @@ get_global_format(hda_afg* afg, multi_format_info* data)
return B_OK; return B_OK;
} }
static status_t static status_t
set_global_format(hda_afg* afg, multi_format_info* data) set_global_format(hda_afg* afg, multi_format_info* data)
{ {
@@ -121,6 +138,7 @@ set_global_format(hda_afg* afg, multi_format_info* data)
return B_OK; return B_OK;
} }
static status_t static status_t
list_mix_controls(hda_afg* afg, multi_mix_control_info * data) list_mix_controls(hda_afg* afg, multi_mix_control_info * data)
{ {
@@ -128,6 +146,7 @@ list_mix_controls(hda_afg* afg, multi_mix_control_info * data)
return B_OK; return B_OK;
} }
static status_t static status_t
list_mix_connections(hda_afg* afg, multi_mix_connection_info * data) list_mix_connections(hda_afg* afg, multi_mix_connection_info * data)
{ {
@@ -135,12 +154,14 @@ list_mix_connections(hda_afg* afg, multi_mix_connection_info * data)
return B_OK; return B_OK;
} }
static status_t static status_t
list_mix_channels(hda_afg* afg, multi_mix_channel_info *data) list_mix_channels(hda_afg* afg, multi_mix_channel_info *data)
{ {
return B_OK; return B_OK;
} }
static status_t static status_t
get_buffers(hda_afg* afg, multi_buffer_list* data) get_buffers(hda_afg* afg, multi_buffer_list* data)
{ {
@@ -196,23 +217,30 @@ get_buffers(hda_afg* afg, multi_buffer_list* data)
/* Setup data structure for multi_audio API... */ /* Setup data structure for multi_audio API... */
data->return_playback_buffers = data->request_playback_buffers; data->return_playback_buffers = data->request_playback_buffers;
data->return_playback_channels = data->request_playback_channels; data->return_playback_channels = data->request_playback_channels;
data->return_playback_buffer_size = data->request_playback_buffer_size; /* frames */ data->return_playback_buffer_size = data->request_playback_buffer_size;
/* frames */
for (bidx=0; bidx < data->return_playback_buffers; bidx++) { for (bidx = 0; bidx < data->return_playback_buffers; bidx++) {
for (cidx=0; cidx < data->return_playback_channels; cidx++) { for (cidx = 0; cidx < data->return_playback_channels; cidx++) {
data->playback_buffers[bidx][cidx].base = afg->playback_stream->buffers[bidx] + (playback_sample_size * cidx); data->playback_buffers[bidx][cidx].base
data->playback_buffers[bidx][cidx].stride = playback_sample_size * data->return_playback_channels; = afg->playback_stream->buffers[bidx]
+ (playback_sample_size * cidx);
data->playback_buffers[bidx][cidx].stride
= playback_sample_size * data->return_playback_channels;
} }
} }
data->return_record_buffers = data->request_record_buffers; data->return_record_buffers = data->request_record_buffers;
data->return_record_channels = data->request_record_channels; data->return_record_channels = data->request_record_channels;
data->return_record_buffer_size = data->request_record_buffer_size; /* frames */ data->return_record_buffer_size = data->request_record_buffer_size;
/* frames */
for (bidx=0; bidx < data->return_record_buffers; bidx++) { for (bidx = 0; bidx < data->return_record_buffers; bidx++) {
for (cidx=0; cidx < data->return_record_channels; cidx++) { for (cidx = 0; cidx < data->return_record_channels; cidx++) {
data->record_buffers[bidx][cidx].base = afg->record_stream->buffers[bidx] + (record_sample_size * cidx); data->record_buffers[bidx][cidx].base
data->record_buffers[bidx][cidx].stride = record_sample_size * data->return_record_channels; = afg->record_stream->buffers[bidx] + (record_sample_size * cidx);
data->record_buffers[bidx][cidx].stride
= record_sample_size * data->return_record_channels;
} }
} }
@@ -229,7 +257,7 @@ buffer_exchange(hda_afg* afg, multi_buffer_info* data)
status_t rc; status_t rc;
if (!afg->playback_stream->running) if (!afg->playback_stream->running)
hda_stream_start(afg->codec->ctrlr, afg->playback_stream); hda_stream_start(afg->codec->controller, afg->playback_stream);
/* do playback */ /* do playback */
rc=acquire_sem(afg->playback_stream->buffer_ready_sem); rc=acquire_sem(afg->playback_stream->buffer_ready_sem);
@@ -260,8 +288,8 @@ buffer_exchange(hda_afg* afg, multi_buffer_info* data)
static status_t static status_t
buffer_force_stop(hda_afg* afg) buffer_force_stop(hda_afg* afg)
{ {
hda_stream_stop(afg->codec->ctrlr, afg->playback_stream); hda_stream_stop(afg->codec->controller, afg->playback_stream);
//hda_stream_stop(afg->codec->ctrlr, afg->record_stream); //hda_stream_stop(afg->codec->controller, afg->record_stream);
delete_sem(afg->playback_stream->buffer_ready_sem); delete_sem(afg->playback_stream->buffer_ready_sem);
// delete_sem(afg->record_stream->buffer_ready_sem); // delete_sem(afg->record_stream->buffer_ready_sem);
@@ -281,27 +309,44 @@ multi_audio_control(void* cookie, uint32 op, void* arg, size_t len)
afg = codec->afgs[0]; afg = codec->afgs[0];
switch(op) { switch (op) {
case B_MULTI_GET_DESCRIPTION: return get_description(afg, arg); case B_MULTI_GET_DESCRIPTION:
case B_MULTI_GET_EVENT_INFO: return B_ERROR; return get_description(afg, arg);
case B_MULTI_SET_EVENT_INFO: return B_ERROR;
case B_MULTI_GET_EVENT: return B_ERROR; case B_MULTI_GET_ENABLED_CHANNELS:
case B_MULTI_GET_ENABLED_CHANNELS: return get_enabled_channels(afg, arg); return get_enabled_channels(afg, arg);
case B_MULTI_SET_ENABLED_CHANNELS: return B_OK; case B_MULTI_SET_ENABLED_CHANNELS:
case B_MULTI_GET_GLOBAL_FORMAT: return get_global_format(afg, arg); return B_OK;
case B_MULTI_SET_GLOBAL_FORMAT: return set_global_format(afg, arg);
case B_MULTI_GET_CHANNEL_FORMATS: return B_ERROR; case B_MULTI_GET_GLOBAL_FORMAT:
case B_MULTI_SET_CHANNEL_FORMATS: return B_ERROR; return get_global_format(afg, arg);
case B_MULTI_GET_MIX: return B_ERROR; case B_MULTI_SET_GLOBAL_FORMAT:
case B_MULTI_SET_MIX: return B_ERROR; return set_global_format(afg, 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_CHANNELS:
case B_MULTI_LIST_MIX_CONNECTIONS: return list_mix_connections(afg, arg); return list_mix_channels(afg, arg);
case B_MULTI_GET_BUFFERS: return get_buffers(afg, arg); case B_MULTI_LIST_MIX_CONTROLS:
case B_MULTI_SET_BUFFERS: return B_ERROR; return list_mix_controls(afg, arg);
case B_MULTI_SET_START_TIME: return B_ERROR; case B_MULTI_LIST_MIX_CONNECTIONS:
case B_MULTI_BUFFER_EXCHANGE: return buffer_exchange(afg, arg); return list_mix_connections(afg, arg);
case B_MULTI_BUFFER_FORCE_STOP: return buffer_force_stop(afg); case B_MULTI_GET_BUFFERS:
return get_buffers(afg, arg);
case B_MULTI_BUFFER_EXCHANGE:
return buffer_exchange(afg, arg);
case B_MULTI_BUFFER_FORCE_STOP:
return buffer_force_stop(afg);
case B_MULTI_GET_EVENT_INFO:
case B_MULTI_SET_EVENT_INFO:
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;
} }
return B_BAD_VALUE; return B_BAD_VALUE;
+48 -29
View File
@@ -1,82 +1,101 @@
/*
* Copyright 2007-2008, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ithamar Adema, ithamar AT unet DOT nl
*/
#include "driver.h" #include "driver.h"
static status_t static status_t
hda_open (const char *name, uint32 flags, void** cookie) hda_open(const char *name, uint32 flags, void** cookie)
{ {
hda_controller* hc = NULL; hda_controller* controller = NULL;
status_t rc = B_OK; status_t rc = B_OK;
long i; long i;
for(i=0; i < num_cards; i++) { for (i = 0; i < gNumCards; i++) {
if (strcmp(cards[i].devfs_path, name) == 0) { if (strcmp(gCards[i].devfs_path, name) == 0) {
hc = &cards[i]; controller = &gCards[i];
break;
} }
} }
if (hc == NULL) if (controller == NULL)
return ENODEV; return ENODEV;
if (hc->opened) if (controller->opened)
return B_BUSY; return B_BUSY;
rc = hda_hw_init(hc); rc = hda_hw_init(controller);
if (rc != B_OK) if (rc != B_OK)
return rc; return rc;
hc->opened++; controller->opened++;
*cookie = hc; *cookie = controller;
return B_OK; return B_OK;
} }
static status_t static status_t
hda_read (void* cookie, off_t position, void *buf, size_t* num_bytes) hda_read(void* cookie, off_t position, void *buf, size_t* numBytes)
{ {
*num_bytes = 0; /* tell caller nothing was read */ *numBytes = 0;
/* tell caller nothing was read */
return B_IO_ERROR; return B_IO_ERROR;
} }
static status_t static status_t
hda_write (void* cookie, off_t position, const void* buffer, size_t* num_bytes) hda_write(void* cookie, off_t position, const void* buffer, size_t* numBytes)
{ {
*num_bytes = 0; /* tell caller nothing was written */ *numBytes = 0;
/* tell caller nothing was written */
return B_IO_ERROR; return B_IO_ERROR;
} }
static status_t static status_t
hda_control (void* cookie, uint32 op, void* arg, size_t len) hda_control(void* cookie, uint32 op, void* arg, size_t length)
{ {
hda_controller* hc = (hda_controller*)cookie; hda_controller* controller = (hda_controller*)cookie;
if (hc->active_codec) if (controller->active_codec)
return multi_audio_control(hc->active_codec, op, arg, len); return multi_audio_control(controller->active_codec, op, arg, length);
return B_BAD_VALUE; return B_BAD_VALUE;
} }
static status_t static status_t
hda_close (void* cookie) hda_close(void* cookie)
{ {
hda_controller* hc = (hda_controller*)cookie; hda_controller* controller = (hda_controller*)cookie;
hda_hw_stop(hc); hda_hw_stop(controller);
--hc->opened; --controller->opened;
return B_OK; return B_OK;
} }
static status_t static status_t
hda_free (void* cookie) hda_free(void* cookie)
{ {
hda_controller* hc = (hda_controller*)cookie; hda_controller* controller = (hda_controller*)cookie;
hda_hw_uninit(hc); hda_hw_uninit(controller);
return B_OK; return B_OK;
} }
device_hooks driver_hooks = {
device_hooks gDriverHooks = {
hda_open, /* -> open entry point */ hda_open, /* -> open entry point */
hda_close, /* -> close entry point */ hda_close, /* -> close entry point */
hda_free, /* -> free cookie */ hda_free, /* -> free cookie */
hda_control, /* -> control entry point */ hda_control, /* -> control entry point */
hda_read, /* -> read entry point */ hda_read, /* -> read entry point */
hda_write /* -> write entry point */ hda_write /* -> write entry point */
}; };