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