* 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"
hda_controller cards[MAXCARDS];
uint32 num_cards;
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
init_hardware(void)
{
pci_info pcii;
status_t rc;
pci_info info;
long i;
if ((rc=get_module(B_PCI_MODULE_NAME, (module_info**)&pci)) == B_OK) {
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) {
put_module(B_PCI_MODULE_NAME);
pci = NULL;
return B_OK;
}
}
if (get_module(B_PCI_MODULE_NAME, (module_info**)&gPci) != B_OK)
return ENODEV;
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;
}
status_t
init_driver (void)
init_driver(void)
{
char path[B_PATH_NAME_LENGTH];
pci_info pcii;
status_t rc;
pci_info info;
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) {
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) {
cards[num_cards].pcii = pcii;
cards[num_cards].opened = 0;
sprintf(path, DEVFS_PATH_FORMAT, num_cards);
cards[num_cards++].devfs_path = strdup(path);
dprintf("HDA: Detected controller @ PCI:%d:%d:%d, IRQ:%d, type %04x/%04x\n",
pcii.bus, pcii.device, pcii.function,
pcii.u.h0.interrupt_line,
pcii.vendor_id, pcii.device_id);
}
gNumCards = 0;
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) {
gCards[gNumCards].pci_info = info;
gCards[gNumCards].opened = 0;
sprintf(path, DEVFS_PATH_FORMAT, gNumCards);
gCards[gNumCards++].devfs_path = strdup(path);
dprintf("HDA: Detected controller @ PCI:%d:%d:%d, IRQ:%d, type %04x/%04x\n",
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);
pci = NULL;
return ENODEV;
}
return B_OK;
}
void
uninit_driver (void)
uninit_driver(void)
{
long i;
dprintf("IRA: %s\n", __func__);
for (i=0; i < num_cards; i++) {
free((void*)cards[i].devfs_path);
cards[i].devfs_path = NULL;
for (i = 0; i < gNumCards; i++) {
free((void*)gCards[i].devfs_path);
gCards[i].devfs_path = NULL;
}
if (pci != NULL) {
put_module(B_PCI_MODULE_NAME);
pci = NULL;
}
put_module(B_PCI_MODULE_NAME);
}
const char**
publish_devices(void)
{
static const char* devs[MAXCARDS+1];
long i;
dprintf("IRA: %s\n", __func__);
for (i=0; i < num_cards; i++)
devs[i] = cards[i].devfs_path;
for (i = 0; i < gNumCards; i++)
devs[i] = gCards[i].devfs_path;
devs[i] = NULL;
return devs;
}
device_hooks*
find_device(const char* name)
{
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_
#define _HDA_H_
@@ -158,7 +165,7 @@ struct hda_codec_s {
hda_afg* afgs[HDA_MAXAFGS];
uint32 num_afgs;
struct hda_controller_s* ctrlr;
struct hda_controller_s* controller;
};
/* hda_controller
@@ -170,7 +177,7 @@ struct hda_codec_s {
*/
struct hda_controller_s {
pci_info pcii;
struct pci_info pci_info;
vuint32 opened;
const char* devfs_path;
@@ -199,30 +206,30 @@ struct hda_controller_s {
};
/* driver.c */
extern device_hooks driver_hooks;
extern pci_module_info* pci;
extern hda_controller cards[MAXCARDS];
extern uint32 num_cards;
extern device_hooks gDriverHooks;
extern pci_module_info* gPci;
extern hda_controller gCards[MAXCARDS];
extern uint32 gNumCards;
/* 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*);
/* 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);
status_t hda_hw_init(hda_controller* controller);
void hda_hw_stop(hda_controller* controller);
void hda_hw_uninit(hda_controller* controller);
status_t hda_send_verbs(hda_codec* codec, corb_t* verbs, uint32* responses, int count);
/* 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);
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);
status_t hda_stream_start(hda_controller* controller, hda_stream* s);
status_t hda_stream_stop(hda_controller* controller, hda_stream* s);
status_t hda_stream_check_intr(hda_controller* controller, hda_stream* s);
#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 "hda_codec_defs.h"
const char* portcon[] = {
static const char* kPortConnector[] = {
"Jack", "None", "Fixed", "Dual"
};
const char* defdev[] = {
"Line Out", "Speaker", "HP Out", "CD", "SPDIF out", "Digital Other Out", "Modem Line Side",
"Modem Hand Side", "Line In", "AUX", "Mic In", "Telephony", "SPDIF In", "Digital Other In",
"Reserved", "Other"
static const char* kDefaultDevice[] = {
"Line Out", "Speaker", "HP Out", "CD", "SPDIF out", "Digital Other Out",
"Modem Line Side", "Modem Hand Side", "Line In", "AUX", "Mic In",
"Telephony", "SPDIF In", "Digital Other In", "Reserved", "Other"
};
const char* conntype[] = {
"N/A", "1/8\"", "1/4\"", "ATAPI internal", "RCA", "Optical", "Other Digital", "Other Analog",
"Multichannel Analog (DIN)", "XLR/Professional", "RJ-11 (Modem)", "Combination", "-", "-", "-", "Other"
static const char* kConnectionType[] = {
"N/A", "1/8\"", "1/4\"", "ATAPI internal", "RCA", "Optical",
"Other Digital", "Other Analog", "Multichannel Analog (DIN)",
"XLR/Professional", "RJ-11 (Modem)", "Combination", "-", "-", "-", "Other"
};
const char* jcolor[] = {
"N/A", "Black", "Grey", "Blue", "Green", "Red", "Orange", "Yellow", "Purple", "Pink",
"-", "-", "-", "-", "White", "Other"
static const char* kJackColor[] = {
"N/A", "Black", "Grey", "Blue", "Green", "Red", "Orange", "Yellow",
"Purple", "Pink", "-", "-", "-", "-", "White", "Other"
};
static status_t
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;
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;
/* 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 << 3)) ;
}
return rc;
}
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];
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[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;
if (resp[2] & (1 << 0)) {
if (resp[1] & (1 << 0)) *rates |= B_SR_8000;
if (resp[1] & (1 << 1)) *rates |= B_SR_11025;
if (resp[1] & (1 << 2)) *rates |= B_SR_16000;
if (resp[1] & (1 << 3)) *rates |= B_SR_22050;
if (resp[1] & (1 << 4)) *rates |= B_SR_32000;
if (resp[1] & (1 << 5)) *rates |= B_SR_44100;
if (resp[1] & (1 << 6)) *rates |= B_SR_48000;
if (resp[1] & (1 << 7)) *rates |= B_SR_88200;
if (resp[1] & (1 << 8)) *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;
if (resp[1] & (1 << 0))
*rates |= B_SR_8000;
if (resp[1] & (1 << 1))
*rates |= B_SR_11025;
if (resp[1] & (1 << 2))
*rates |= B_SR_16000;
if (resp[1] & (1 << 3))
*rates |= B_SR_22050;
if (resp[1] & (1 << 4))
*rates |= B_SR_32000;
if (resp[1] & (1 << 5))
*rates |= B_SR_44100;
if (resp[1] & (1 << 6))
*rates |= B_SR_48000;
if (resp[1] & (1 << 7))
*rates |= B_SR_88200;
if (resp[1] & (1 << 8))
*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 << 2)) /* Sort out how to handle AC3 */;
//FIXME: if (resp[0] & (1 << 1)) *fmts |= B_FMT_FLOAT;
//FIXME: if (resp[0] & (1 << 2)) /* Sort out how to handle AC3 */;
}
return rc;
}
static status_t
hda_widget_get_amplifier_capabilities(hda_codec* codec, uint32 nid)
{
status_t rc;
corb_t verb;
uint32 resp;
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) {
verb = MAKE_VERB(codec->addr, nid, VID_GET_PARAM, PID_OUTPUT_AMP_CAP);
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",
(resp & (1 << 31)) ? "supported" : "N/A",
(resp >> 16) & 0x7F,
(resp >> 8) & 0x7F,
resp & 0x7F);
}
return rc;
}
static status_t
hda_codec_parse_afg(hda_afg* afg)
{
@@ -107,20 +142,25 @@ hda_codec_parse_afg(hda_afg* afg)
uint32 resp[6];
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);
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);
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(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");
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");
dprintf("%s: #GPIO: %ld, #GPO: %ld, #GPI: %ld, unsol: %s, wake: %s\n", __func__,
resp[4] & 0xFF, (resp[1] >> 8) & 0xFF, (resp[1] >> 16) & 0xFF,
(resp[1] & (1 << 30)) ? "yes" : "no",
dprintf("%s: #GPIO: %ld, #GPO: %ld, #GPI: %ld, unsol: %s, wake: %s\n",
__func__, resp[4] & 0xFF, (resp[1] >> 8) & 0xFF,
(resp[1] >> 16) & 0xFF, (resp[1] & (1 << 30)) ? "yes" : "no",
(resp[1] & (1 << 31)) ? "yes" : "no");
afg->wid_start = resp[2] >> 16;
@@ -133,20 +173,24 @@ hda_codec_parse_afg(hda_afg* afg)
}
/* 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;
char buf[256];
int off;
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);
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);
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] ");
if (resp[0] & (1 << 11))
off += sprintf(buf + off, "[L-R Swap] ");
if (resp[0] & (1 << 10)) {
corb_t verb;
uint32 resp;
@@ -154,21 +198,30 @@ hda_codec_parse_afg(hda_afg* afg)
off += sprintf(buf+off, "[Power] ");
/* 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);
}
if (resp[0] & (1 << 9)) off += sprintf(buf+off, "[Digital] ");
if (resp[0] & (1 << 7)) off += sprintf(buf+off, "[Unsol Capable] ");
if (resp[0] & (1 << 6)) off += sprintf(buf+off, "[Proc Widget] ");
if (resp[0] & (1 << 5)) off += sprintf(buf+off, "[Stripe] ");
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] ");
if (resp[0] & (1 << 9))
off += sprintf(buf + off, "[Digital] ");
if (resp[0] & (1 << 7))
off += sprintf(buf + off, "[Unsol Capable] ");
if (resp[0] & (1 << 6))
off += sprintf(buf + off, "[Proc Widget] ");
if (resp[0] & (1 << 5))
off += sprintf(buf + off, "[Stripe] ");
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:
dprintf("%ld:\tAudio Output\n", wid);
hda_widget_get_stream_support(afg->codec, wid,
@@ -193,11 +246,12 @@ hda_codec_parse_afg(hda_afg* afg)
break;
case WT_PIN_COMPLEX:
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) {
afg->widgets[widx].d.pin.input = resp[0] & (1 << 5);
afg->widgets[widx].d.pin.output = resp[0] & (1 << 4);
dprintf("\t%s%s\n",
afg->widgets[widx].d.pin.input ? "[Input] " : "",
afg->widgets[widx].d.pin.input ? "[Output]" : "");
@@ -205,16 +259,17 @@ hda_codec_parse_afg(hda_afg* afg)
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) {
afg->widgets[widx].d.pin.device = (resp[0] >> 20) & 0xF;
dprintf("\t%s, %s, %s, %s\n",
portcon[resp[0] >> 30],
defdev[afg->widgets[widx].d.pin.device],
conntype[(resp[0] >> 16) & 0xF],
jcolor[(resp[0] >> 12) & 0xF]);
kPortConnector[resp[0] >> 30],
kDefaultDevice[afg->widgets[widx].d.pin.device],
kConnectionType[(resp[0] >> 16) & 0xF],
kJackColor[(resp[0] >> 12) & 0xF]);
}
hda_widget_get_amplifier_capabilities(afg->codec, wid);
break;
case WT_POWER:
@@ -236,61 +291,66 @@ hda_codec_parse_afg(hda_afg* afg)
dprintf("\t%s\n", buf);
hda_widget_get_pm_support(afg->codec, wid, &afg->widgets[widx].pm);
if (afg->widgets[widx].num_inputs) {
int idx;
off = 0;
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)
afg->widgets[widx].active_input = resp[0] & 0xFF;
else
afg->widgets[widx].active_input = -1;
} else
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)) {
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) {
dprintf("%s: Error parsing inputs for widget %ld!\n", __func__, wid);
dprintf("%s: Error parsing inputs for widget %ld!\n",
__func__, wid);
break;
}
}
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);
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);
}
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);
}
}
}
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.
*/
static uint32
hda_codec_afg_find_path(hda_afg* afg, uint32 wid, uint32 wtype, uint32 depth)
{
int widx = wid - afg->wid_start;
int idx;
switch(afg->widgets[widx].type) {
switch (afg->widgets[widx].type) {
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 (afg->widgets[widx].active_input == -1)
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;
case WT_AUDIO_SELECTOR:
{
int idx = afg->widgets[widx].active_input;
if (idx != -1) {
uint32 wid = afg->widgets[widx].inputs[idx];
if (hda_codec_afg_find_path(afg, wid, wtype, depth +1)) {
return wid;
}
{
int idx = afg->widgets[widx].active_input;
if (idx != -1) {
uint32 wid = afg->widgets[widx].inputs[idx];
if (hda_codec_afg_find_path(afg, wid, wtype, depth + 1)) {
return wid;
}
}
break;
}
default:
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;
}
static void
hda_afg_delete(hda_afg* afg)
{
@@ -339,6 +400,7 @@ hda_afg_delete(hda_afg* afg)
}
}
static status_t
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;
uint32 idx;
if ((afg=calloc(1, sizeof(hda_afg))) == NULL) {
if ((afg = calloc(1, sizeof(hda_afg))) == NULL) {
rc = B_NO_MEMORY;
goto done;
}
@@ -360,94 +422,110 @@ hda_codec_afg_new(hda_codec* codec, uint32 afg_nid)
if (rc != B_OK)
goto free_afg;
/* Setup for worst-case scenario;
we cannot find any output Pin Widgets */
/* Setup for worst-case scenario; we cannot find any output Pin Widgets */
rc = ENODEV;
/* 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;
int32 iidx;
if (afg->playback_stream == NULL && afg->widgets[idx].type == WT_PIN_COMPLEX && afg->widgets[idx].d.pin.output) {
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)
{
iidx = afg->widgets[idx].active_input;
if (iidx != -1) {
output_wid = hda_codec_afg_find_path(afg, afg->widgets[idx].inputs[iidx], WT_AUDIO_OUTPUT, 0);
} else {
for (iidx=0; iidx < afg->widgets[idx].num_inputs; iidx++) {
output_wid = hda_codec_afg_find_path(afg, afg->widgets[idx].inputs[iidx], WT_AUDIO_OUTPUT, 0);
if (output_wid) {
corb_t verb = MAKE_VERB(codec->addr,idx+afg->wid_start,VID_SET_CONNSEL,iidx);
if (hda_send_verbs(codec, &verb, NULL, 1) != B_OK)
dprintf("%s: Setting output selector failed!\n", __func__);
break;
if (afg->playback_stream == NULL
&& afg->widgets[idx].type == WT_PIN_COMPLEX
&& afg->widgets[idx].d.pin.output) {
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) {
iidx = afg->widgets[idx].active_input;
if (iidx != -1) {
output_wid = hda_codec_afg_find_path(afg,
afg->widgets[idx].inputs[iidx], WT_AUDIO_OUTPUT, 0);
} else {
for (iidx = 0; iidx < afg->widgets[idx].num_inputs; iidx++) {
output_wid = hda_codec_afg_find_path(afg,
afg->widgets[idx].inputs[iidx], WT_AUDIO_OUTPUT, 0);
if (output_wid) {
corb_t verb = MAKE_VERB(codec->addr,
idx + afg->wid_start, VID_SET_CONNSEL, iidx);
if (hda_send_verbs(codec, &verb, NULL, 1) != B_OK)
dprintf("%s: 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",
__func__, defdev[afg->widgets[idx].d.pin.device], output_wid);
}
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->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) {
iidx = afg->widgets[idx].active_input;
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 {
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);
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);
if (input_wid) {
corb_t verb = MAKE_VERB(codec->addr,idx+afg->wid_start,VID_SET_CONNSEL,iidx);
if (hda_send_verbs(codec, &verb, NULL, 1) != B_OK)
dprintf("%s: Setting input selector failed!\n", __func__);
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 input selector failed!\n",
__func__);
}
break;
}
}
}
if (input_wid) {
if (!afg->record_stream) {
corb_t verb;
/* 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->io_wid = idx + afg->wid_start;
/* FIXME: Force Pin Widget to unmute */
verb = MAKE_VERB(codec->addr, afg->record_stream->pin_wid,
VID_SET_AMPGAINMUTE, (1 << 15) | (1 << 13) | (1 << 12));
hda_send_verbs(codec, &verb, NULL, 1);
}
dprintf("%s: Found input PIN (%s) connected to input CONV wid:%ld\n",
__func__, defdev[afg->widgets[input_wid-afg->wid_start].d.pin.device], idx+afg->wid_start);
dprintf("%s: Found input PIN (%s) connected to input CONV "
"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;
}
void
hda_codec_delete(hda_codec* codec)
{
@@ -474,7 +553,7 @@ hda_codec_delete(hda_codec* codec)
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]);
codec->afgs[idx] = NULL;
}
@@ -483,44 +562,45 @@ hda_codec_delete(hda_codec* 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));
uint32 responses[3];
corb_t verbs[3];
status_t rc;
uint32 nid;
if (codec == NULL) goto exit_new;
if (codec == NULL)
goto exit_new;
codec->ctrlr = ctrlr;
codec->controller = controller;
codec->addr = cad;
codec->response_sem = create_sem(0, "hda_codec_response_sem");
ctrlr->codecs[cad] = codec;
verbs[0] = MAKE_VERB(cad,0,VID_GET_PARAM,PID_VENDORID);
verbs[1] = MAKE_VERB(cad,0,VID_GET_PARAM,PID_REVISIONID);
verbs[2] = MAKE_VERB(cad,0,VID_GET_PARAM,PID_SUBORD_NODE_COUNT);
controller->codecs[cad] = codec;
verbs[0] = MAKE_VERB(cad, 0, VID_GET_PARAM, PID_VENDORID);
verbs[1] = MAKE_VERB(cad, 0, VID_GET_PARAM, PID_REVISIONID);
verbs[2] = MAKE_VERB(cad, 0, VID_GET_PARAM, PID_SUBORD_NODE_COUNT);
if (hda_send_verbs(codec, verbs, responses, 3) != B_OK)
goto cmd_failed;
dprintf("Codec %ld Vendor: %04lx Product: %04lx\n",
cad, responses[0] >> 16, responses[0] & 0xFFFF);
for (nid=responses[2] >> 16;
nid < (responses[2] >> 16) + (responses[2] & 0xFF);
nid++) {
for (nid = responses[2] >> 16;
nid < (responses[2] >> 16) + (responses[2] & 0xFF); nid++) {
uint32 resp;
verbs[0] = MAKE_VERB(cad,nid,VID_GET_PARAM,PID_FUNCGRP_TYPE);
if ((rc=hda_send_verbs(codec, verbs, &resp, 1)) != B_OK)
verbs[0] = MAKE_VERB(cad, nid, VID_GET_PARAM, PID_FUNCGRP_TYPE);
if (hda_send_verbs(codec, verbs, &resp, 1) != B_OK)
goto cmd_failed;
if ((resp&0xFF) == 1) {
if ((resp & 0xFF) == 1) {
/* 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",
__func__, strerror(rc));
goto cmd_failed;
@@ -531,7 +611,7 @@ hda_codec_new(hda_controller* ctrlr, uint32 cad)
goto exit_new;
cmd_failed:
ctrlr->codecs[cad] = NULL;
controller->codecs[cad] = NULL;
hda_codec_delete(codec);
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
#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 "hda_controller_defs.h"
#include "hda_codec_defs.h"
#include "driver.h"
void
hda_stream_delete(hda_stream* s)
hda_stream_delete(hda_stream* stream)
{
if (s->buffer_ready_sem >= B_OK)
delete_sem(s->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);
if (stream->buffer_ready_sem >= B_OK)
delete_sem(stream->buffer_ready_sem);
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_new(hda_controller* ctrlr, int type)
hda_stream_new(hda_controller* controller, int type)
{
hda_stream* s = calloc(1, sizeof(hda_stream));
if (s != NULL) {
s->buffer_area = B_ERROR;
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;
hda_stream* stream = calloc(1, sizeof(hda_stream));
if (stream == NULL)
return NULL;
default:
dprintf("%s: Unknown stream type %d!\n", __func__, type);
free(s);
s = NULL;
break;
}
stream->buffer_area = B_ERROR;
stream->bdl_area = B_ERROR;
switch (type) {
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);
s->running = true;
stream->running = true;
return B_OK;
}
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) {
uint8 sts = OREG8(ctrlr,s->off,STS);
if (stream->running) {
uint8 sts = OREG8(controller, stream->off, STS);
if (sts) {
cpu_status status;
OREG8(ctrlr,s->off,STS) = sts;
OREG8(controller, stream->off, STS) = sts;
status = disable_interrupts();
acquire_spinlock(&s->lock);
acquire_spinlock(&stream->lock);
s->real_time = system_time();
s->frames_count += s->buffer_length;
s->buffer_cycle = (s->buffer_cycle +1) % s->num_buffers;
stream->real_time = system_time();
stream->frames_count += stream->buffer_length;
stream->buffer_cycle = (stream->buffer_cycle + 1)
% stream->num_buffers;
release_spinlock(&s->lock);
release_spinlock(&stream->lock);
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;
}
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);
s->running = false;
stream->running = false;
return B_OK;
}
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 response[2], idx;
uint32 bufferSize, bufferPhysicalAddress, alloc;
uint32 response[2], index;
physical_entry pe;
bdl_entry_t* bdl;
corb_t verb[2];
@@ -120,237 +135,301 @@ hda_stream_setup_buffers(hda_afg* afg, hda_stream* s, const char* desc)
uint16 wfmt;
/* Clear previously allocated memory */
if (s->buffer_area >= B_OK) {
delete_area(s->buffer_area);
s->buffer_area = B_ERROR;
if (stream->buffer_area >= B_OK) {
delete_area(stream->buffer_area);
stream->buffer_area = B_ERROR;
}
if (s->bdl_area >= B_OK) {
delete_area(s->bdl_area);
s->bdl_area = B_ERROR;
if (stream->bdl_area >= B_OK) {
delete_area(stream->bdl_area);
stream->bdl_area = B_ERROR;
}
/* Calculate size of buffer (aligned to 128 bytes) */
buffer_size = s->sample_size * s->num_channels * s->buffer_length;
buffer_size = (buffer_size + 127) & (~127);
bufferSize = stream->sample_size * stream->num_channels
* stream->buffer_length;
bufferSize = (bufferSize + 127) & (~127);
/* 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));
/* 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);
if (s->buffer_area < B_OK)
return s->buffer_area;
stream->buffer_area = create_area("hda_buffers", (void**)&buffer,
B_ANY_KERNEL_ADDRESS, alloc, B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA);
if (stream->buffer_area < B_OK)
return stream->buffer_area;
/* Get the physical address of memory */
rc = get_memory_map(buffer, alloc, &pe, 1);
if (rc != B_OK) {
delete_area(s->buffer_area);
delete_area(stream->buffer_area);
return rc;
}
buffer_pa = (uint32)pe.address;
bufferPhysicalAddress = (uint32)pe.address;
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) */
for (idx=0; idx < s->num_buffers; idx++) {
s->buffers[idx] = buffer + (idx*buffer_size);
s->buffers_pa[idx] = buffer_pa + (idx*buffer_size);
for (index = 0; index < stream->num_buffers; index++) {
stream->buffers[index] = buffer + (index * bufferSize);
stream->buffers_pa[index] = bufferPhysicalAddress + (index * bufferSize);
}
/* Now allocate BDL for buffer range */
alloc = s->num_buffers * sizeof(bdl_entry_t);
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);
if (s->bdl_area < B_OK) {
delete_area(s->buffer_area);
return s->bdl_area;
alloc = stream->num_buffers * sizeof(bdl_entry_t);
alloc = (alloc + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
stream->bdl_area = create_area("hda_bdl", (void**)&bdl,
B_ANY_KERNEL_ADDRESS, alloc, B_CONTIGUOUS, 0);
if (stream->bdl_area < B_OK) {
delete_area(stream->buffer_area);
return stream->bdl_area;
}
/* Get the physical address of memory */
rc = get_memory_map(bdl, alloc, &pe, 1);
if (rc != B_OK) {
delete_area(s->buffer_area);
delete_area(s->bdl_area);
delete_area(stream->buffer_area);
delete_area(stream->bdl_area);
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,
alloc, s->num_buffers);
alloc, stream->num_buffers);
/* Setup BDL entries */
for (idx=0; idx < s->num_buffers; idx++, bdl++) {
bdl->address = s->buffers_pa[idx];
bdl->length = buffer_size;
for (index = 0; index < stream->num_buffers; index++, bdl++) {
bdl->address = stream->buffers_pa[index];
bdl->length = bufferSize;
bdl->ioc = 1;
}
/* Configure stream registers */
wfmt = s->num_channels -1;
switch(s->sampleformat) {
case B_FMT_8BIT_S: wfmt |= (0 << 4); s->bps = 8; break;
case B_FMT_16BIT: wfmt |= (1 << 4); s->bps = 16; break;
case B_FMT_24BIT: wfmt |= (3 << 4); s->bps = 24; break;
case B_FMT_32BIT: wfmt |= (4 << 4); s->bps = 32; break;
default: dprintf("%s: Invalid sample format: 0x%lx\n", __func__, s->sampleformat); break;
wfmt = stream->num_channels -1;
switch (stream->sampleformat) {
case B_FMT_8BIT_S: wfmt |= (0 << 4); stream->bps = 8; break;
case B_FMT_16BIT: wfmt |= (1 << 4); stream->bps = 16; break;
case B_FMT_24BIT: wfmt |= (3 << 4); stream->bps = 24; break;
case B_FMT_32BIT: wfmt |= (4 << 4); stream->bps = 32; break;
default:
dprintf("%s: Invalid sample format: 0x%lx\n", __func__,
stream->sampleformat);
break;
}
switch(s->samplerate) {
case B_SR_8000: wfmt |= (0 << 14) | (0 << 11) | (5 << 8); s->rate=8000; break;
case B_SR_11025: wfmt |= (1 << 14) | (0 << 11) | (3 << 8); s->rate=11025; break;
case B_SR_16000: wfmt |= (0 << 14) | (0 << 11) | (2 << 8); s->rate=16000; break;
case B_SR_22050: wfmt |= (1 << 14) | (0 << 11) | (1 << 8); s->rate=22050; break;
case B_SR_32000: wfmt |= (0 << 14) | (1 << 11) | (2 << 8); s->rate=32000; break;
case B_SR_44100: wfmt |= (1 << 14) | (0 << 11) | (0 << 8); s->rate=44100; break;
case B_SR_48000: wfmt |= (0 << 14) | (0 << 11) | (0 << 8); s->rate=48000; break;
case B_SR_88200: wfmt |= (1 << 14) | (1 << 11) | (0 << 8); s->rate=88200; break;
case B_SR_96000: wfmt |= (0 << 14) | (2 << 11) | (0 << 8); s->rate=96000; break;
case B_SR_176400: wfmt |= (1 << 14) | (3 << 11) | (0 << 8); s->rate=176400; break;
case B_SR_192000: wfmt |= (0 << 14) | (3 << 11) | (0 << 8); s->rate=192000; break;
default: dprintf("%s: Invalid sample rate: 0x%lx\n", __func__, s->samplerate); break;
switch (stream->samplerate) {
case B_SR_8000:
wfmt |= (0 << 14) | (0 << 11) | (5 << 8);
stream->rate = 8000;
break;
case B_SR_11025:
wfmt |= (1 << 14) | (0 << 11) | (3 << 8);
stream->rate = 11025;
break;
case B_SR_16000:
wfmt |= (0 << 14) | (0 << 11) | (2 << 8);
stream->rate = 16000;
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;
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->sample_size * s->num_channels * s->num_buffers * s->buffer_length; /* total cyclic buffer size in _bytes_ */
OREG8(afg->codec->ctrlr,s->off,CTL0) = CTL0_IOCE | CTL0_FEIE | CTL0_DEIE;
OREG8(afg->codec->ctrlr,s->off,CTL2) = s->id << 4;
OREG16(afg->codec->controller, stream->off, FMT) = wfmt;
OREG32(afg->codec->controller, stream->off, BDPL) = stream->bdl_pa;
OREG32(afg->codec->controller, stream->off, BDPU) = 0;
OREG16(afg->codec->controller, stream->off, LVI) = stream->num_buffers -1;
/* total cyclic buffer size in _bytes_ */
OREG32(afg->codec->controller, stream->off, CBL) = stream->sample_size
* 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[1] = MAKE_VERB(afg->codec->addr, s->io_wid, VID_SET_CVTSTRCHN, s->id << 4);
verb[0] = MAKE_VERB(afg->codec->addr, stream->io_wid, VID_SET_CONVFORMAT,
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);
return rc;
}
//#pragma mark -
// #pragma mark -
status_t
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;
codec->response_count = 0;
memcpy(corb+(codec->ctrlr->corbwp +1), verbs, sizeof(corb_t)*count);
REG16(codec->ctrlr,CORBWP) = (codec->ctrlr->corbwp += count);
memcpy(corb + (codec->controller->corbwp + 1), verbs,
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)
memcpy(responses, codec->responses, count*sizeof(uint32));
memcpy(responses, codec->responses, count * sizeof(uint32));
return rc;
}
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 */
uint32 intsts = REG32(ctrlr, INTSTS);
if (intsts & INTSTS_GIS) {
rc = B_HANDLED_INTERRUPT;
uint32 intsts = REG32(controller, INTSTS);
if ((intsts & INTSTS_GIS) == 0)
return B_UNHANDLED_INTERRUPT;
/* Controller or stream related? */
if (intsts & INTSTS_CIS) {
uint32 statests = REG16(ctrlr,STATESTS);
uint8 rirbsts = REG8(ctrlr,RIRBSTS);
uint8 corbsts = REG8(ctrlr,CORBSTS);
/* Controller or stream related? */
if (intsts & INTSTS_CIS) {
uint32 statests = REG16(controller, STATESTS);
uint8 rirbsts = REG8(controller, RIRBSTS);
uint8 corbsts = REG8(controller, CORBSTS);
if (statests) {
/* Detected Codec state change */
REG16(ctrlr,STATESTS) = statests;
ctrlr->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 (statests) {
/* Detected Codec state change */
REG16(controller, STATESTS) = statests;
controller->codecsts = statests;
}
if (intsts & ~(INTSTS_CIS|INTSTS_GIS)) {
int idx;
for (idx=0; idx < HDA_MAXSTREAMS; idx++) {
if (intsts & (1 << idx)) {
if (ctrlr->streams[idx])
hda_stream_check_intr(ctrlr, ctrlr->streams[idx]);
else
dprintf("%s: Stream interrupt for unconfigured stream %d!\n", __func__, idx);
}
/* Check for incoming responses */
if (rirbsts) {
REG8(controller, RIRBSTS) = rirbsts;
if (rirbsts & RIRBSTS_RINTFL) {
uint16 rirbwp = REG16(controller, RIRBWP);
while (controller->rirbrp <= rirbwp) {
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;
}
static status_t
hda_hw_start(hda_controller* ctrlr)
hda_hw_start(hda_controller* controller)
{
int timeout = 10;
/* Put controller out of reset mode */
REG32(ctrlr,GCTL) |= GCTL_CRST;
REG32(controller, GCTL) |= GCTL_CRST;
do {
snooze(100);
} while (--timeout && !(REG32(ctrlr,GCTL) & GCTL_CRST));
} while (--timeout && !(REG32(controller, GCTL) & GCTL_CRST));
return timeout ? B_OK : B_TIMED_OUT;
}
static status_t
hda_hw_corb_rirb_init(hda_controller* ctrlr)
hda_hw_corb_rirb_init(hda_controller* controller)
{
uint32 memsz, rirboff;
uint8 corbsz, rirbsz;
@@ -358,174 +437,171 @@ hda_hw_corb_rirb_init(hda_controller* ctrlr)
physical_entry pe;
/* Determine and set size of CORB */
corbsz = REG8(ctrlr,CORBSIZE);
corbsz = REG8(controller, CORBSIZE);
if (corbsz & CORBSIZE_CAP_256E) {
ctrlr->corblen = 256;
REG8(ctrlr,CORBSIZE) = CORBSIZE_SZ_256E;
controller->corblen = 256;
REG8(controller, CORBSIZE) = CORBSIZE_SZ_256E;
} else if (corbsz & CORBSIZE_CAP_16E) {
ctrlr->corblen = 16;
REG8(ctrlr,CORBSIZE) = CORBSIZE_SZ_16E;
controller->corblen = 16;
REG8(controller, CORBSIZE) = CORBSIZE_SZ_16E;
} else if (corbsz & CORBSIZE_CAP_2E) {
ctrlr->corblen = 2;
REG8(ctrlr,CORBSIZE) = CORBSIZE_SZ_2E;
controller->corblen = 2;
REG8(controller, CORBSIZE) = CORBSIZE_SZ_2E;
}
/* Determine and set size of RIRB */
rirbsz = REG8(ctrlr,RIRBSIZE);
rirbsz = REG8(controller, RIRBSIZE);
if (rirbsz & RIRBSIZE_CAP_256E) {
ctrlr->rirblen = 256;
REG8(ctrlr,RIRBSIZE) = RIRBSIZE_SZ_256E;
controller->rirblen = 256;
REG8(controller, RIRBSIZE) = RIRBSIZE_SZ_256E;
} else if (rirbsz & RIRBSIZE_CAP_16E) {
ctrlr->rirblen = 16;
REG8(ctrlr,RIRBSIZE) = RIRBSIZE_SZ_16E;
controller->rirblen = 16;
REG8(controller, RIRBSIZE) = RIRBSIZE_SZ_16E;
} else if (rirbsz & RIRBSIZE_CAP_2E) {
ctrlr->rirblen = 2;
REG8(ctrlr,RIRBSIZE) = RIRBSIZE_SZ_2E;
controller->rirblen = 2;
REG8(controller, RIRBSIZE) = RIRBSIZE_SZ_2E;
}
/* Determine rirb offset in memory and total size of corb+alignment+rirb */
rirboff = (ctrlr->corblen * sizeof(corb_t) + 0x7f) & ~0x7f;
memsz = ((B_PAGE_SIZE -1) +
rirboff +
(ctrlr->rirblen * sizeof(rirb_t))) & ~(B_PAGE_SIZE-1);
rirboff = (controller->corblen * sizeof(corb_t) + 0x7f) & ~0x7f;
memsz = (rirboff + controller->rirblen * sizeof(rirb_t) + B_PAGE_SIZE - 1)
& ~(B_PAGE_SIZE - 1);
/* Allocate memory area */
ctrlr->rb_area = create_area("hda_corb_rirb",
(void**)&ctrlr->corb, B_ANY_KERNEL_ADDRESS, memsz, B_CONTIGUOUS, 0);
if (ctrlr->rb_area < 0) {
return ctrlr->rb_area;
}
controller->rb_area = create_area("hda_corb_rirb", (void**)&controller->corb,
B_ANY_KERNEL_ADDRESS, memsz, B_CONTIGUOUS, 0);
if (controller->rb_area < 0)
return controller->rb_area;
/* Rirb is after corb+aligment */
ctrlr->rirb = (rirb_t*)(((uint8*)ctrlr->corb)+rirboff);
if ((rc=get_memory_map(ctrlr->corb, memsz, &pe, 1)) != B_OK) {
delete_area(ctrlr->rb_area);
controller->rirb = (rirb_t*)(((uint8*)controller->corb) + rirboff);
if ((rc = get_memory_map(controller->corb, memsz, &pe, 1)) != B_OK) {
delete_area(controller->rb_area);
return rc;
}
/* Program CORB/RIRB for these locations */
REG32(ctrlr,CORBLBASE) = (uint32)pe.address;
REG32(ctrlr,RIRBLBASE) = (uint32)pe.address + rirboff;
REG32(controller, CORBLBASE) = (uint32)pe.address;
REG32(controller, RIRBLBASE) = (uint32)pe.address + rirboff;
/* Reset CORB read pointer */
/* NOTE: See HDA011 for corrected procedure! */
REG16(ctrlr,CORBRP) = CORBRP_RST;
REG16(controller, CORBRP) = CORBRP_RST;
do {
snooze(10);
} while ( !(REG16(ctrlr,CORBRP) & CORBRP_RST) );
REG16(ctrlr,CORBRP) = 0;
} while ( !(REG16(controller, CORBRP) & CORBRP_RST) );
REG16(controller, CORBRP) = 0;
/* Reset RIRB write pointer */
REG16(ctrlr,RIRBWP) = RIRBWP_RST;
REG16(controller, RIRBWP) = RIRBWP_RST;
/* Generate interrupt for every response */
REG16(ctrlr,RINTCNT) = 1;
REG16(controller, RINTCNT) = 1;
/* Setup cached read/write indices */
ctrlr->rirbrp = 1;
ctrlr->corbwp = 0;
controller->rirbrp = 1;
controller->corbwp = 0;
/* Gentlemen, start your engines... */
REG8(ctrlr,CORBCTL) = CORBCTL_RUN | CORBCTL_MEIE;
REG8(ctrlr,RIRBCTL) = RIRBCTL_DMAEN | RIRBCTL_OIC | RIRBCTL_RINTCTL;
REG8(controller, CORBCTL) = CORBCTL_RUN | CORBCTL_MEIE;
REG8(controller, RIRBCTL) = RIRBCTL_DMAEN | RIRBCTL_OIC | RIRBCTL_RINTCTL;
return B_OK;
}
// #pragma mark -
//#pragma mark -
/* Setup hardware for use; detect codecs; etc */
/*! Setup hardware for use; detect codecs; etc */
status_t
hda_hw_init(hda_controller* ctrlr)
hda_hw_init(hda_controller* controller)
{
status_t rc;
uint16 gcap;
uint32 idx;
uint32 index;
/* Map MMIO registers */
ctrlr->regs_area =
map_physical_memory("hda_hw_regs", (void*)ctrlr->pcii.u.h0.base_registers[0],
ctrlr->pcii.u.h0.base_register_sizes[0], B_ANY_KERNEL_ADDRESS, 0,
(void**)&ctrlr->regs);
if (ctrlr->regs_area < B_OK) {
rc = ctrlr->regs_area;
controller->regs_area = map_physical_memory("hda_hw_regs",
(void*)controller->pci_info.u.h0.base_registers[0],
controller->pci_info.u.h0.base_register_sizes[0], B_ANY_KERNEL_ADDRESS,
0, (void**)&controller->regs);
if (controller->regs_area < B_OK) {
rc = controller->regs_area;
goto error;
}
/* Absolute minimum hw is online; we can now install interrupt handler */
ctrlr->irq = ctrlr->pcii.u.h0.interrupt_line;
rc = install_io_interrupt_handler(ctrlr->irq,
(interrupt_handler)hda_interrupt_handler, ctrlr, 0);
controller->irq = controller->pci_info.u.h0.interrupt_line;
rc = install_io_interrupt_handler(controller->irq,
(interrupt_handler)hda_interrupt_handler, controller, 0);
if (rc != B_OK)
goto no_irq;
/* 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",
REG8(ctrlr,VMAJ), REG8(ctrlr,VMIN),
REG8(controller, VMAJ), REG8(controller, VMIN),
GCAP_OSS(gcap), GCAP_ISS(gcap), GCAP_BSS(gcap),
GCAP_NSDO(gcap) ? GCAP_NSDO(gcap) *2 : 1,
gcap & GCAP_64OK ? "yes" : "no" );
ctrlr->num_input_streams = GCAP_OSS(gcap);
ctrlr->num_output_streams = GCAP_ISS(gcap);
ctrlr->num_bidir_streams = GCAP_BSS(gcap);
controller->num_input_streams = GCAP_OSS(gcap);
controller->num_output_streams = GCAP_ISS(gcap);
controller->num_bidir_streams = GCAP_BSS(gcap);
/* Get controller into valid state */
rc = hda_hw_start(ctrlr);
rc = hda_hw_start(controller);
if (rc != B_OK)
goto reset_failed;
/* Setup CORB/RIRB */
rc = hda_hw_corb_rirb_init(ctrlr);
rc = hda_hw_corb_rirb_init(controller);
if (rc != B_OK)
goto corb_rirb_failed;
REG16(ctrlr,WAKEEN) = 0x7fff;
REG16(controller, WAKEEN) = 0x7fff;
/* 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 */
snooze(1000);
if (!ctrlr->codecsts) {
if (!controller->codecsts) {
rc = ENODEV;
goto corb_rirb_failed;
}
for (idx=0; idx < HDA_MAXCODECS; idx++)
if (ctrlr->codecsts & (1 << idx))
hda_codec_new(ctrlr, idx);
for (index = 0; index < HDA_MAXCODECS; index++) {
if ((controller->codecsts & (1 << index)) != 0)
hda_codec_new(controller, index);
}
for (idx=0; idx < HDA_MAXCODECS; idx++) {
if (ctrlr->codecs[idx] && ctrlr->codecs[idx]->num_afgs) {
ctrlr->active_codec = ctrlr->codecs[idx];
for (index = 0; index < HDA_MAXCODECS; index++) {
if (controller->codecs[index] && controller->codecs[index]->num_afgs) {
controller->active_codec = controller->codecs[index];
break;
}
}
if (ctrlr->active_codec != NULL)
if (controller->active_codec != NULL)
return B_OK;
else
rc = ENODEV;
rc = ENODEV;
corb_rirb_failed:
REG32(ctrlr,INTCTL) = 0;
REG32(controller, INTCTL) = 0;
reset_failed:
remove_io_interrupt_handler(ctrlr->irq,
(interrupt_handler)hda_interrupt_handler,
ctrlr);
remove_io_interrupt_handler(controller->irq,
(interrupt_handler)hda_interrupt_handler, controller);
no_irq:
delete_area(ctrlr->regs_area);
ctrlr->regs_area = B_ERROR;
ctrlr->regs = NULL;
delete_area(controller->regs_area);
controller->regs_area = B_ERROR;
controller->regs = NULL;
error:
dprintf("ERROR: %s(%ld)\n", strerror(rc), rc);
@@ -533,57 +609,61 @@ error:
return rc;
}
/* Stop any activity */
/*! Stop any activity */
void
hda_hw_stop(hda_controller* ctrlr)
hda_hw_stop(hda_controller* controller)
{
int idx;
int index;
/* Stop all audio streams */
for (idx=0; idx < HDA_MAXSTREAMS; idx++)
if (ctrlr->streams[idx] && ctrlr->streams[idx]->running)
hda_stream_stop(ctrlr, ctrlr->streams[idx]);
for (index = 0; index < HDA_MAXSTREAMS; index++)
if (controller->streams[index] && controller->streams[index]->running)
hda_stream_stop(controller, controller->streams[index]);
}
/* Free resources */
/*! Free resources */
void
hda_hw_uninit(hda_controller* ctrlr)
hda_hw_uninit(hda_controller* controller)
{
if (ctrlr != NULL) {
uint32 idx;
uint32 index;
/* Stop all audio streams */
hda_hw_stop(ctrlr);
/* Stop CORB/RIRB */
REG8(ctrlr,CORBCTL) = 0;
REG8(ctrlr,RIRBCTL) = 0;
if (controller == NULL)
return;
/* Disable interrupts and remove interrupt handler */
REG32(ctrlr,INTCTL) = 0;
REG32(ctrlr,GCTL) &= ~GCTL_CRST;
remove_io_interrupt_handler(ctrlr->irq,
(interrupt_handler)hda_interrupt_handler,
ctrlr);
/* Stop all audio streams */
hda_hw_stop(controller);
/* Delete corb/rirb area */
if (ctrlr->rb_area >= 0) {
delete_area(ctrlr->rb_area);
ctrlr->rb_area = B_ERROR;
ctrlr->corb = NULL;
ctrlr->rirb = NULL;
}
/* Unmap registers */
if (ctrlr->regs_area >= 0) {
delete_area(ctrlr->regs_area);
ctrlr->regs_area = B_ERROR;
ctrlr->regs = NULL;
}
/* Now delete all codecs */
for (idx=0; idx < HDA_MAXCODECS; idx++)
if (ctrlr->codecs[idx] != NULL)
hda_codec_delete(ctrlr->codecs[idx]);
/* Stop CORB/RIRB */
REG8(controller, CORBCTL) = 0;
REG8(controller, RIRBCTL) = 0;
/* Disable interrupts and remove interrupt handler */
REG32(controller, INTCTL) = 0;
REG32(controller, GCTL) &= ~GCTL_CRST;
remove_io_interrupt_handler(controller->irq,
(interrupt_handler)hda_interrupt_handler, controller);
/* Delete corb/rirb area */
if (controller->rb_area >= 0) {
delete_area(controller->rb_area);
controller->rb_area = B_ERROR;
controller->corb = NULL;
controller->rirb = NULL;
}
/* 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
#define HDAC_REGS_H
#include <SupportDefs.h>
/* Accessors for HDA controller registers */
#define REG32(ctrlr,reg) (*(vuint32*)((ctrlr)->regs + HDAC_##reg))
#define REG16(ctrlr,reg) (*(vuint16*)((ctrlr)->regs + HDAC_##reg))
#define REG8(ctrlr,reg) (*((ctrlr)->regs + HDAC_##reg))
#define REG32(controller, reg) (*(vuint32*)((controller)->regs + HDAC_##reg))
#define REG16(controller, reg) (*(vuint16*)((controller)->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 OREG16(ctrlr,s,reg) (*(vuint16*)((ctrlr)->regs + HDAC_SDBASE + (s) + HDAC_SD_##reg))
#define OREG8(ctrlr,s,reg) (*((ctrlr)->regs + HDAC_SDBASE + (s) + HDAC_SD_##reg))
#define OREG32(controller, stream, reg) \
(*(vuint32*)((controller)->regs + HDAC_SDBASE + (stream) + 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 */
#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 "driver.h"
multi_channel_info chans[] = {
static multi_channel_info sChannels[] = {
{ 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 },
{ 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 },
};
static int32
format2size(uint32 format)
{
switch(format) {
switch (format) {
case B_FMT_8BIT_S:
case B_FMT_16BIT:
return 2;
@@ -27,20 +38,21 @@ format2size(uint32 format)
case B_FMT_FLOAT:
return 8;
default:
return -1;
}
}
static status_t
get_description(hda_afg* afg, multi_description* data)
{
data->interface_version = B_CURRENT_INTERFACE_VERSION;
data->interface_minimum = B_CURRENT_INTERFACE_VERSION;
strcpy(data->friendly_name,"HD Audio");
strcpy(data->vendor_info,"Haiku");
strcpy(data->friendly_name, "HD Audio");
strcpy(data->vendor_info, "Haiku");
data->output_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->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]))) {
memcpy(data->channels,&chans,sizeof(chans));
if (data->request_channel_count >= (int)(sizeof(sChannels)
/ sizeof(sChannels[0]))) {
memcpy(data->channels, &sChannels, sizeof(sChannels));
}
/* 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->start_latency = 30000;
strcpy(data->control_panel,"");
strcpy(data->control_panel, "");
return B_OK;
}
static status_t
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;
}
static status_t
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;
}
static status_t
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;
}
static status_t
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;
}
static status_t
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;
}
static status_t
list_mix_channels(hda_afg* afg, multi_mix_channel_info *data)
{
return B_OK;
}
static status_t
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... */
data->return_playback_buffers = data->request_playback_buffers;
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 (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].stride = playback_sample_size * data->return_playback_channels;
for (bidx = 0; bidx < data->return_playback_buffers; bidx++) {
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].stride
= playback_sample_size * data->return_playback_channels;
}
}
data->return_record_buffers = data->request_record_buffers;
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 (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].stride = record_sample_size * data->return_record_channels;
for (bidx = 0; bidx < data->return_record_buffers; bidx++) {
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].stride
= record_sample_size * data->return_record_channels;
}
}
@@ -229,7 +257,7 @@ buffer_exchange(hda_afg* afg, multi_buffer_info* data)
status_t rc;
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 */
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
buffer_force_stop(hda_afg* afg)
{
hda_stream_stop(afg->codec->ctrlr, afg->playback_stream);
//hda_stream_stop(afg->codec->ctrlr, afg->record_stream);
hda_stream_stop(afg->codec->controller, afg->playback_stream);
//hda_stream_stop(afg->codec->controller, afg->record_stream);
delete_sem(afg->playback_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];
switch(op) {
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(afg, arg);
case B_MULTI_SET_ENABLED_CHANNELS: return B_OK;
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(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(afg, arg);
case B_MULTI_BUFFER_FORCE_STOP: return buffer_force_stop(afg);
switch (op) {
case B_MULTI_GET_DESCRIPTION:
return get_description(afg, 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(afg, arg);
case B_MULTI_SET_GLOBAL_FORMAT:
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_CONNECTIONS:
return list_mix_connections(afg, arg);
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;
+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"
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;
long i;
for(i=0; i < num_cards; i++) {
if (strcmp(cards[i].devfs_path, name) == 0) {
hc = &cards[i];
for (i = 0; i < gNumCards; i++) {
if (strcmp(gCards[i].devfs_path, name) == 0) {
controller = &gCards[i];
break;
}
}
if (hc == NULL)
if (controller == NULL)
return ENODEV;
if (hc->opened)
if (controller->opened)
return B_BUSY;
rc = hda_hw_init(hc);
rc = hda_hw_init(controller);
if (rc != B_OK)
return rc;
hc->opened++;
controller->opened++;
*cookie = hc;
*cookie = controller;
return B_OK;
}
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;
}
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;
}
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;
if (hc->active_codec)
return multi_audio_control(hc->active_codec, op, arg, len);
hda_controller* controller = (hda_controller*)cookie;
if (controller->active_codec)
return multi_audio_control(controller->active_codec, op, arg, length);
return B_BAD_VALUE;
}
static status_t
hda_close (void* cookie)
hda_close(void* cookie)
{
hda_controller* hc = (hda_controller*)cookie;
hda_hw_stop(hc);
--hc->opened;
hda_controller* controller = (hda_controller*)cookie;
hda_hw_stop(controller);
--controller->opened;
return B_OK;
}
static status_t
hda_free (void* cookie)
hda_free(void* cookie)
{
hda_controller* hc = (hda_controller*)cookie;
hda_hw_uninit(hc);
hda_controller* controller = (hda_controller*)cookie;
hda_hw_uninit(controller);
return B_OK;
}
device_hooks driver_hooks = {
device_hooks gDriverHooks = {
hda_open, /* -> open entry point */
hda_close, /* -> close entry point */
hda_free, /* -> free cookie */
hda_control, /* -> control entry point */
hda_control, /* -> control entry point */
hda_read, /* -> read entry point */
hda_write /* -> write entry point */
};