* 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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user