Coding style cleanup, also fixed some minor bugs I found during that work:

* hda_widget_get_stream_support() did reference an out-of-bounds response for
  the stream support parameter.
* Bit 18 in the PCM support parameter is 20bit sample support, not 18bit support.
* Enabled reporting B_FMT_FLOAT capability (not tested at all yet).
* Fixed B_FMT_FLOAT size (float is only 4 bytes, not 8).
* No longer clobbers the request_* parameters in B_MULTI_GET_BUFFERS.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24117 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2008-02-25 16:07:01 +00:00
parent 877c1bc7fb
commit 4919a4dc6f
6 changed files with 711 additions and 632 deletions
@@ -12,7 +12,7 @@
int32 api_version = B_CUR_DRIVER_API_VERSION; int32 api_version = B_CUR_DRIVER_API_VERSION;
hda_controller gCards[MAXCARDS]; hda_controller gCards[MAX_CARDS];
uint32 gNumCards; uint32 gNumCards;
pci_module_info* gPci; pci_module_info* gPci;
@@ -51,7 +51,8 @@ init_driver(void)
gNumCards = 0; gNumCards = 0;
for (i = 0; gPci->get_nth_pci_info(i, &info) == B_OK; i++) { for (i = 0; gPci->get_nth_pci_info(i, &info) == B_OK
&& gNumCards < MAX_CARDS; i++) {
if (info.class_base == PCI_multimedia if (info.class_base == PCI_multimedia
&& info.class_sub == PCI_hd_audio) { && info.class_sub == PCI_hd_audio) {
gCards[gNumCards].pci_info = info; gCards[gNumCards].pci_info = info;
@@ -93,7 +94,7 @@ uninit_driver(void)
const char** const char**
publish_devices(void) publish_devices(void)
{ {
static const char* devs[MAXCARDS+1]; static const char* devs[MAX_CARDS + 1];
long i; long i;
dprintf("IRA: %s\n", __func__); dprintf("IRA: %s\n", __func__);
+95 -96
View File
@@ -8,48 +8,48 @@
#ifndef _HDA_H_ #ifndef _HDA_H_
#define _HDA_H_ #define _HDA_H_
#include <drivers/KernelExport.h> #include <KernelExport.h>
#include <drivers/Drivers.h> #include <Drivers.h>
#include <drivers/PCI.h> #include <PCI.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#ifndef HAIKU_TARGET_PLATFORM_HAIKU #ifndef HAIKU_TARGET_PLATFORM_HAIKU
#define DEVFS_PATH_FORMAT "audio/multi/hda/%lu" # define DEVFS_PATH_FORMAT "audio/multi/hda/%lu"
#include <multi_audio.h> # include <multi_audio.h>
#else #else
#define DEVFS_PATH_FORMAT "audio/hmulti/hda/%lu" # define DEVFS_PATH_FORMAT "audio/hmulti/hda/%lu"
#include <hmulti_audio.h> # include <hmulti_audio.h>
#endif #endif
#include "hda_controller_defs.h" #include "hda_controller_defs.h"
#include "hda_codec_defs.h" #include "hda_codec_defs.h"
#define MAXCARDS 4 #define MAX_CARDS 4
/* values for the class_sub field for class_base = 0x04 (multimedia device) */ /* values for the class_sub field for class_base = 0x04 (multimedia device) */
#define PCI_hd_audio 3 #define PCI_hd_audio 3
#define HDA_MAXAFGS 15 #define HDA_MAX_AUDIO_GROUPS 15
#define HDA_MAXCODECS 15 #define HDA_MAX_CODECS 15
#define HDA_MAXSTREAMS 16 #define HDA_MAX_STREAMS 16
#define MAX_CODEC_RESPONSES 10 #define MAX_CODEC_RESPONSES 10
#define MAXINPUTS 32 #define MAX_INPUTS 32
/* FIXME: Find out why we need so much! */ /* FIXME: Find out why we need so much! */
#define DEFAULT_FRAMESPERBUF 4096 #define DEFAULT_FRAMES_PER_BUFFER 4096
typedef struct hda_controller_s hda_controller; typedef struct hda_controller hda_controller;
typedef struct hda_codec_s hda_codec; typedef struct hda_codec hda_codec;
typedef struct hda_afg_s hda_afg; typedef struct hda_audio_group hda_audio_group;
#define STRMAXBUF 10 #define STREAM_MAX_BUFFERS 10
#define STRMINBUF 2 #define STREAM_MIN_BUFFERS 2
enum { enum {
STRM_PLAYBACK, STREAM_PLAYBACK,
STRM_RECORD STREAM_RECORD
}; };
/* hda_stream_info /* hda_stream_info
@@ -58,24 +58,26 @@ enum {
* which is can have multiple channels (for stereo or better). * which is can have multiple channels (for stereo or better).
*/ */
typedef struct hda_stream_info_s { typedef struct hda_stream_info {
uint32 id; /* HDA controller stream # */ uint32 id; /* HDA controller stream # */
uint32 off; /* HDA I/O/B descriptor offset */ uint32 off; /* HDA I/O/B descriptor offset */
bool running; /* Is this stream active? */ bool running; /* Is this stream active? */
spinlock lock; /* Write lock */ spinlock lock; /* Write lock */
uint32 pin_wid; /* PIN Widget ID */ uint32 pin_widget; /* PIN Widget ID */
uint32 io_wid; /* Input/Output Converter Widget ID */ uint32 io_widget; /* Input/Output Converter Widget ID */
uint32 samplerate; uint32 sample_rate;
uint32 sampleformat; uint32 sample_format;
uint32 num_buffers; uint32 num_buffers;
uint32 num_channels; uint32 num_channels;
uint32 buffer_length; /* size of buffer in samples */ uint32 buffer_length; /* size of buffer in samples */
uint32 sample_size; uint32 sample_size;
void* buffers[STRMAXBUF]; /* Virtual addresses for buffer */ void* buffers[STREAM_MAX_BUFFERS];
uint32 buffers_pa[STRMAXBUF]; /* Physical addresses for buffer */ /* Virtual addresses for buffer */
uint32 physical_buffers[STREAM_MAX_BUFFERS];
/* Physical addresses for buffer */
sem_id buffer_ready_sem; sem_id buffer_ready_sem;
bigtime_t real_time; bigtime_t real_time;
uint32 frames_count; uint32 frames_count;
@@ -84,38 +86,36 @@ typedef struct hda_stream_info_s {
uint32 rate, bps; /* Samplerate & bits per sample */ uint32 rate, bps; /* Samplerate & bits per sample */
area_id buffer_area; area_id buffer_area;
area_id bdl_area; area_id buffer_descriptors_area;
uint32 bdl_pa; /* BDL physical address */ uint32 physical_buffer_descriptors; /* BDL physical address */
} hda_stream; } hda_stream;
/* hda_afg /* hda_audio_group
* *
* This structure describes a single Audio Function Group. An afg * This structure describes a single Audio Function Group. An afg
* is a group of audio widgets which can be used to configure multiple * is a group of audio widgets which can be used to configure multiple
* streams of audio either from the HDA Link to an output device (= playback) * streams of audio either from the HDA Link to an output device (= playback)
* or from an input device to the HDA link (= recording). * or from an input device to the HDA link (= recording).
*/ */
struct hda_audio_group {
struct hda_afg_s { hda_codec* codec;
hda_codec* codec;
/* Multi Audio API data */ /* Multi Audio API data */
hda_stream* playback_stream; hda_stream* playback_stream;
hda_stream* record_stream; hda_stream* record_stream;
uint32 root_node_id;
uint32 root_nid, uint32 widget_start;
wid_start, uint32 widget_count;
wid_count;
uint32 supported_formats;
uint32 deffmts, uint32 supported_rates;
defrates, uint32 supported_pm;
defpm;
struct { struct {
uint32 num_inputs; uint32 num_inputs;
int32 active_input; int32 active_input;
uint32 inputs[MAXINPUTS]; uint32 inputs[MAX_INPUTS];
uint32 flags; uint32 flags;
hda_widget_type type; hda_widget_type type;
@@ -133,9 +133,9 @@ struct hda_afg_s {
struct { struct {
} mixer; } mixer;
struct { struct {
uint32 output; uint32 output;
uint32 input; uint32 input;
pin_dev_type device; pin_dev_type device;
} pin; } pin;
} d; } d;
} *widgets; } *widgets;
@@ -150,22 +150,21 @@ struct hda_afg_s {
* *
* NOTE: Atm, only Audio Function Groups are supported. * NOTE: Atm, only Audio Function Groups are supported.
*/ */
struct hda_codec {
uint16 vendor_id;
uint16 product_id;
uint8 revision;
uint16 stepping;
uint8 addr;
struct hda_codec_s { sem_id response_sem;
uint16 vendor_id; uint32 responses[MAX_CODEC_RESPONSES];
uint16 product_id; uint32 response_count;
uint8 hda_rev;
uint16 rev_stepping;
uint8 addr;
sem_id response_sem; hda_audio_group* audio_groups[HDA_MAX_AUDIO_GROUPS];
uint32 responses[MAX_CODEC_RESPONSES]; uint32 num_audio_groups;
uint32 response_count;
hda_afg* afgs[HDA_MAXAFGS]; struct hda_controller* controller;
uint32 num_afgs;
struct hda_controller_s* controller;
}; };
/* hda_controller /* hda_controller
@@ -175,61 +174,61 @@ struct hda_codec_s {
* for use by the codecs contained, and the messaging queue * for use by the codecs contained, and the messaging queue
* (verb/response) buffers for communication. * (verb/response) buffers for communication.
*/ */
struct hda_controller {
struct hda_controller_s {
struct pci_info pci_info; struct pci_info pci_info;
vuint32 opened; vuint32 opened;
const char* devfs_path; const char* devfs_path;
area_id regs_area;
vuint8* regs;
uint32 irq;
uint16 codecsts; area_id regs_area;
uint32 num_input_streams; vuint8* regs;
uint32 num_output_streams; uint32 irq;
uint32 num_bidir_streams;
uint32 corblen;
uint32 rirblen;
uint32 rirbrp;
uint32 corbwp;
area_id rb_area;
corb_t* corb;
rirb_t* rirb;
hda_codec* codecs[HDA_MAXCODECS]; uint16 codecsts;
uint32 num_input_streams;
uint32 num_output_streams;
uint32 num_bidir_streams;
uint32 corb_length;
uint32 rirb_length;
uint32 rirbrp;
uint32 corbwp;
area_id rb_area;
corb_t* corb;
rirb_t* rirb;
hda_codec* codecs[HDA_MAX_CODECS];
hda_codec* active_codec; hda_codec* active_codec;
uint32 num_codecs; uint32 num_codecs;
hda_stream* streams[HDA_MAXSTREAMS]; hda_stream* streams[HDA_MAX_STREAMS];
}; };
/* driver.c */ /* driver.c */
extern device_hooks gDriverHooks; extern device_hooks gDriverHooks;
extern pci_module_info* gPci; extern pci_module_info* gPci;
extern hda_controller gCards[MAXCARDS]; extern hda_controller gCards[MAX_CARDS];
extern uint32 gNumCards; extern uint32 gNumCards;
/* hda_codec.c */ /* hda_codec.c */
hda_codec* hda_codec_new(hda_controller* controller, uint32 cad); hda_codec* hda_codec_new(hda_controller* controller, uint32 cad);
void hda_codec_delete(hda_codec*); void hda_codec_delete(hda_codec* 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 length);
/* hda_controller.c: Basic controller support */ /* hda_controller.c: Basic controller support */
status_t hda_hw_init(hda_controller* controller); status_t hda_hw_init(hda_controller* controller);
void hda_hw_stop(hda_controller* controller); void hda_hw_stop(hda_controller* controller);
void hda_hw_uninit(hda_controller* controller); void hda_hw_uninit(hda_controller* controller);
status_t hda_send_verbs(hda_codec* codec, corb_t* verbs, uint32* responses, int count); 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* controller, 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* stream);
status_t hda_stream_setup_buffers(hda_afg* afg, hda_stream* s, const char* desc); status_t hda_stream_setup_buffers(hda_audio_group* audioGroup,
status_t hda_stream_start(hda_controller* controller, hda_stream* s); hda_stream* stream, const char* desc);
status_t hda_stream_stop(hda_controller* controller, hda_stream* s); status_t hda_stream_start(hda_controller* controller, hda_stream* stream);
status_t hda_stream_check_intr(hda_controller* controller, hda_stream* s); status_t hda_stream_stop(hda_controller* controller, hda_stream* stream);
#endif /* _HDA_H_ */ #endif /* _HDA_H_ */
+343 -323
View File
@@ -34,9 +34,9 @@ static const char* kJackColor[] = {
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 nodeID, uint32* pm)
{ {
corb_t verb = MAKE_VERB(codec->addr, nid, VID_GET_PARAM, corb_t verb = MAKE_VERB(codec->addr, nodeID, VID_GET_PARAM,
PID_POWERSTATE_SUPPORT); PID_POWERSTATE_SUPPORT);
status_t rc; status_t rc;
uint32 resp; uint32 resp;
@@ -56,72 +56,77 @@ hda_widget_get_pm_support(hda_codec* codec, uint32 nid, uint32* pm)
static status_t static status_t
hda_widget_get_stream_support(hda_codec* codec, uint32 nid, uint32* fmts, hda_widget_get_stream_support(hda_codec* codec, uint32 nodeID, uint32* formats,
uint32* rates) uint32* rates)
{ {
corb_t verbs[2]; corb_t verbs[2];
uint32 resp[2]; uint32 resp[2];
status_t rc; status_t status;
verbs[0] = MAKE_VERB(codec->addr,nid,VID_GET_PARAM,PID_STREAM_SUPPORT); verbs[0] = MAKE_VERB(codec->addr, nodeID, 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, nodeID, VID_GET_PARAM, PID_PCM_SUPPORT);
if ((rc = hda_send_verbs(codec, verbs, resp, 2)) == B_OK) {
*fmts = 0; *rates = 0;
if (resp[2] & (1 << 0)) { status = hda_send_verbs(codec, verbs, resp, 2);
if (resp[1] & (1 << 0)) if (status != B_OK)
*rates |= B_SR_8000; return status;
if (resp[1] & (1 << 1))
*rates |= B_SR_11025;
if (resp[1] & (1 << 2))
*rates |= B_SR_16000;
if (resp[1] & (1 << 3))
*rates |= B_SR_22050;
if (resp[1] & (1 << 4))
*rates |= B_SR_32000;
if (resp[1] & (1 << 5))
*rates |= B_SR_44100;
if (resp[1] & (1 << 6))
*rates |= B_SR_48000;
if (resp[1] & (1 << 7))
*rates |= B_SR_88200;
if (resp[1] & (1 << 8))
*rates |= B_SR_96000;
if (resp[1] & (1 << 9))
*rates |= B_SR_176400;
if (resp[1] & (1 << 10))
*rates |= B_SR_192000;
if (resp[1] & (1 << 11))
*rates |= B_SR_384000;
if (resp[1] & (1 << 16)) *formats = 0;
*fmts |= B_FMT_8BIT_S; *rates = 0;
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; if ((resp[0] & (STREAM_FLOAT | STREAM_PCM)) != 0) {
//FIXME: if (resp[0] & (1 << 2)) /* Sort out how to handle AC3 */; if (resp[1] & (1 << 0))
*rates |= B_SR_8000;
if (resp[1] & (1 << 1))
*rates |= B_SR_11025;
if (resp[1] & (1 << 2))
*rates |= B_SR_16000;
if (resp[1] & (1 << 3))
*rates |= B_SR_22050;
if (resp[1] & (1 << 4))
*rates |= B_SR_32000;
if (resp[1] & (1 << 5))
*rates |= B_SR_44100;
if (resp[1] & (1 << 6))
*rates |= B_SR_48000;
if (resp[1] & (1 << 7))
*rates |= B_SR_88200;
if (resp[1] & (1 << 8))
*rates |= B_SR_96000;
if (resp[1] & (1 << 9))
*rates |= B_SR_176400;
if (resp[1] & (1 << 10))
*rates |= B_SR_192000;
if (resp[1] & (1 << 11))
*rates |= B_SR_384000;
if (resp[1] & PCM_8_BIT)
*formats |= B_FMT_8BIT_S;
if (resp[1] & PCM_16_BIT)
*formats |= B_FMT_16BIT;
if (resp[1] & PCM_20_BIT)
*formats |= B_FMT_20BIT;
if (resp[1] & PCM_24_BIT)
*formats |= B_FMT_24BIT;
if (resp[1] & PCM_32_BIT)
*formats |= B_FMT_32BIT;
} }
if ((resp[0] & STREAM_FLOAT) != 0)
*formats |= B_FMT_FLOAT;
return rc; //FIXME: if (resp[0] & (1 << 2)) /* Sort out how to handle AC3 */;
return B_OK;
} }
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 nodeID)
{ {
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, nodeID, VID_GET_PARAM, PID_OUTPUT_AMP_CAP);
rc = hda_send_verbs(codec, &verb, &resp, 1); rc = hda_send_verbs(codec, &verb, &resp, 1);
if (rc == B_OK && resp != 0) { 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",
@@ -136,202 +141,207 @@ hda_widget_get_amplifier_capabilities(hda_codec* codec, uint32 nid)
static status_t static status_t
hda_codec_parse_afg(hda_afg* afg) hda_codec_parse_audio_group(hda_audio_group* audioGroup)
{ {
corb_t verbs[6]; corb_t verbs[6];
uint32 resp[6]; uint32 resp[6];
uint32 widx; uint32 widx;
hda_widget_get_stream_support(afg->codec, afg->root_nid, &afg->deffmts, hda_widget_get_stream_support(audioGroup->codec, audioGroup->root_node_id,
&afg->defrates); &audioGroup->supported_formats, &audioGroup->supported_rates);
hda_widget_get_pm_support(afg->codec, afg->root_nid, &afg->defpm); hda_widget_get_pm_support(audioGroup->codec, audioGroup->root_node_id,
&audioGroup->supported_pm);
verbs[0] = MAKE_VERB(afg->codec->addr, afg->root_nid, VID_GET_PARAM, verbs[0] = MAKE_VERB(audioGroup->codec->addr, audioGroup->root_node_id,
PID_AUDIO_FG_CAP); VID_GET_PARAM, PID_AUDIO_FG_CAP);
verbs[1] = MAKE_VERB(afg->codec->addr, afg->root_nid, VID_GET_PARAM, verbs[1] = MAKE_VERB(audioGroup->codec->addr, audioGroup->root_node_id,
PID_GPIO_COUNT); VID_GET_PARAM, PID_GPIO_COUNT);
verbs[2] = MAKE_VERB(afg->codec->addr, afg->root_nid, VID_GET_PARAM, verbs[2] = MAKE_VERB(audioGroup->codec->addr, audioGroup->root_node_id,
PID_SUBORD_NODE_COUNT); VID_GET_PARAM, PID_SUBORD_NODE_COUNT);
if (hda_send_verbs(afg->codec, verbs, resp, 3) == B_OK) { if (hda_send_verbs(audioGroup->codec, verbs, resp, 3) != B_OK)
dprintf("%s: Output delay: %ld samples, Input delay: %ld samples, " return B_ERROR;
"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", dprintf("%s: Output delay: %ld samples, Input delay: %ld samples, "
__func__, resp[4] & 0xFF, (resp[1] >> 8) & 0xFF, "Beep Generator: %s\n", __func__, resp[0] & 0xf,
(resp[1] >> 16) & 0xFF, (resp[1] & (1 << 30)) ? "yes" : "no", (resp[0] >> 8) & 0xf, (resp[0] & (1 << 16)) ? "yes" : "no");
(resp[1] & (1 << 31)) ? "yes" : "no");
afg->wid_start = resp[2] >> 16; dprintf("%s: #GPIO: %ld, #GPO: %ld, #GPI: %ld, unsol: %s, wake: %s\n",
afg->wid_count = resp[2] & 0xFF; __func__, resp[4] & 0xFF, (resp[1] >> 8) & 0xFF,
(resp[1] >> 16) & 0xFF, (resp[1] & (1 << 30)) ? "yes" : "no",
(resp[1] & (1 << 31)) ? "yes" : "no");
afg->widgets = calloc(afg->wid_count, sizeof(*afg->widgets)); audioGroup->widget_start = resp[2] >> 16;
if (afg->widgets == NULL) { audioGroup->widget_count = resp[2] & 0xFF;
dprintf("ERROR: Not enough memory!\n");
return B_NO_MEMORY; audioGroup->widgets = calloc(audioGroup->widget_count,
sizeof(*audioGroup->widgets));
if (audioGroup->widgets == NULL) {
dprintf("ERROR: Not enough memory!\n");
return B_NO_MEMORY;
}
/* Iterate over all Widgets and collect info */
for (widx = 0; widx < audioGroup->widget_count; widx++) {
uint32 wid = audioGroup->widget_start + widx;
char buf[256];
int off;
verbs[0] = MAKE_VERB(audioGroup->codec->addr, wid, VID_GET_PARAM,
PID_AUDIO_WIDGET_CAP);
verbs[1] = MAKE_VERB(audioGroup->codec->addr, wid, VID_GET_PARAM,
PID_CONNLIST_LEN);
hda_send_verbs(audioGroup->codec, verbs, resp, 2);
audioGroup->widgets[widx].type = resp[0] >> 20;
audioGroup->widgets[widx].num_inputs = resp[1] & 0x7F;
off = 0;
if (resp[0] & (1 << 11))
off += sprintf(buf + off, "[L-R Swap] ");
if (resp[0] & (1 << 10)) {
corb_t verb;
uint32 resp;
off += sprintf(buf+off, "[Power] ");
/* We support power; switch us on! */
verb = MAKE_VERB(audioGroup->codec->addr, wid,
VID_SET_POWERSTATE, 0);
hda_send_verbs(audioGroup->codec, &verb, &resp, 1);
} }
/* Iterate over all Widgets and collect info */ if (resp[0] & (1 << 9))
for (widx = 0; widx < afg->wid_count; widx++) { off += sprintf(buf + off, "[Digital] ");
uint32 wid = afg->wid_start + widx; if (resp[0] & (1 << 7))
char buf[256]; off += sprintf(buf + off, "[Unsol Capable] ");
int off; if (resp[0] & (1 << 6))
off += sprintf(buf + off, "[Proc Widget] ");
if (resp[0] & (1 << 5))
off += sprintf(buf + off, "[Stripe] ");
if (resp[0] & (1 << 4))
off += sprintf(buf + off, "[Format Override] ");
if (resp[0] & (1 << 3))
off += sprintf(buf + off, "[Amp Param Override] ");
if (resp[0] & (1 << 2))
off += sprintf(buf + off, "[Out Amp] ");
if (resp[0] & (1 << 1))
off += sprintf(buf + off, "[In Amp] ");
if (resp[0] & (1 << 0))
off += sprintf(buf + off, "[Stereo] ");
verbs[0] = MAKE_VERB(afg->codec->addr, wid, VID_GET_PARAM, switch (audioGroup->widgets[widx].type) {
PID_AUDIO_WIDGET_CAP); case WT_AUDIO_OUTPUT:
verbs[1] = MAKE_VERB(afg->codec->addr, wid, VID_GET_PARAM, dprintf("%ld:\tAudio Output\n", wid);
PID_CONNLIST_LEN); hda_widget_get_stream_support(audioGroup->codec, wid,
hda_send_verbs(afg->codec, verbs, resp, 2); &audioGroup->widgets[widx].d.input.formats,
&audioGroup->widgets[widx].d.input.rates);
hda_widget_get_amplifier_capabilities(audioGroup->codec, wid);
break;
case WT_AUDIO_INPUT:
dprintf("%ld:\tAudio Input\n", wid);
hda_widget_get_stream_support(audioGroup->codec, wid,
&audioGroup->widgets[widx].d.input.formats,
&audioGroup->widgets[widx].d.input.rates);
hda_widget_get_amplifier_capabilities(audioGroup->codec, wid);
break;
case WT_AUDIO_MIXER:
dprintf("%ld:\tAudio Mixer\n", wid);
hda_widget_get_amplifier_capabilities(audioGroup->codec, wid);
break;
case WT_AUDIO_SELECTOR:
dprintf("%ld:\tAudio Selector\n", wid);
hda_widget_get_amplifier_capabilities(audioGroup->codec, wid);
break;
case WT_PIN_COMPLEX:
dprintf("%ld:\tPin Complex\n", wid);
verbs[0] = MAKE_VERB(audioGroup->codec->addr, wid, VID_GET_PARAM,
PID_PIN_CAP);
if (hda_send_verbs(audioGroup->codec, verbs, resp, 1) == B_OK) {
audioGroup->widgets[widx].d.pin.input = resp[0] & (1 << 5);
audioGroup->widgets[widx].d.pin.output = resp[0] & (1 << 4);
afg->widgets[widx].type = resp[0] >> 20; dprintf("\t%s%s\n",
afg->widgets[widx].num_inputs = resp[1] & 0x7F; audioGroup->widgets[widx].d.pin.input ? "[Input] " : "",
audioGroup->widgets[widx].d.pin.input ? "[Output]" : "");
off = 0; } else {
if (resp[0] & (1 << 11)) dprintf("%s: Error getting Pin Complex IO\n", __func__);
off += sprintf(buf + off, "[L-R Swap] ");
if (resp[0] & (1 << 10)) {
corb_t verb;
uint32 resp;
off += sprintf(buf+off, "[Power] ");
/* We support power; switch us on! */
verb = MAKE_VERB(afg->codec->addr, wid, VID_SET_POWERSTATE, 0);
hda_send_verbs(afg->codec, &verb, &resp, 1);
}
if (resp[0] & (1 << 9))
off += sprintf(buf + off, "[Digital] ");
if (resp[0] & (1 << 7))
off += sprintf(buf + off, "[Unsol Capable] ");
if (resp[0] & (1 << 6))
off += sprintf(buf + off, "[Proc Widget] ");
if (resp[0] & (1 << 5))
off += sprintf(buf + off, "[Stripe] ");
if (resp[0] & (1 << 4))
off += sprintf(buf + off, "[Format Override] ");
if (resp[0] & (1 << 3))
off += sprintf(buf + off, "[Amp Param Override] ");
if (resp[0] & (1 << 2))
off += sprintf(buf + off, "[Out Amp] ");
if (resp[0] & (1 << 1))
off += sprintf(buf + off, "[In Amp] ");
if (resp[0] & (1 << 0))
off += sprintf(buf + off, "[Stereo] ");
switch (afg->widgets[widx].type) {
case WT_AUDIO_OUTPUT:
dprintf("%ld:\tAudio Output\n", wid);
hda_widget_get_stream_support(afg->codec, wid,
&afg->widgets[widx].d.input.formats,
&afg->widgets[widx].d.input.rates);
hda_widget_get_amplifier_capabilities(afg->codec, wid);
break;
case WT_AUDIO_INPUT:
dprintf("%ld:\tAudio Input\n", wid);
hda_widget_get_stream_support(afg->codec, wid,
&afg->widgets[widx].d.input.formats,
&afg->widgets[widx].d.input.rates);
hda_widget_get_amplifier_capabilities(afg->codec, wid);
break;
case WT_AUDIO_MIXER:
dprintf("%ld:\tAudio Mixer\n", wid);
hda_widget_get_amplifier_capabilities(afg->codec, wid);
break;
case WT_AUDIO_SELECTOR:
dprintf("%ld:\tAudio Selector\n", wid);
hda_widget_get_amplifier_capabilities(afg->codec, wid);
break;
case WT_PIN_COMPLEX:
dprintf("%ld:\tPin Complex\n", wid);
verbs[0] = MAKE_VERB(afg->codec->addr, wid, VID_GET_PARAM,
PID_PIN_CAP);
if (hda_send_verbs(afg->codec, verbs, resp, 1) == B_OK) {
afg->widgets[widx].d.pin.input = resp[0] & (1 << 5);
afg->widgets[widx].d.pin.output = resp[0] & (1 << 4);
dprintf("\t%s%s\n",
afg->widgets[widx].d.pin.input ? "[Input] " : "",
afg->widgets[widx].d.pin.input ? "[Output]" : "");
} else {
dprintf("%s: Error getting Pin Complex IO\n", __func__);
}
verbs[0] = MAKE_VERB(afg->codec->addr, wid,
VID_GET_CFGDEFAULT, 0);
if (hda_send_verbs(afg->codec, verbs, resp, 1) == B_OK) {
afg->widgets[widx].d.pin.device = (resp[0] >> 20) & 0xF;
dprintf("\t%s, %s, %s, %s\n",
kPortConnector[resp[0] >> 30],
kDefaultDevice[afg->widgets[widx].d.pin.device],
kConnectionType[(resp[0] >> 16) & 0xF],
kJackColor[(resp[0] >> 12) & 0xF]);
}
hda_widget_get_amplifier_capabilities(afg->codec, wid);
break;
case WT_POWER:
dprintf("%ld:\tPower\n", wid);
break;
case WT_VOLUME_KNOB:
dprintf("%ld:\tVolume Knob\n", wid);
break;
case WT_BEEP_GENERATOR:
dprintf("%ld:\tBeep Generator\n", wid);
break;
case WT_VENDOR_DEFINED:
dprintf("%ld:\tVendor Defined\n", wid);
break;
default: /* Reserved */
break;
}
dprintf("\t%s\n", buf);
hda_widget_get_pm_support(afg->codec, wid, &afg->widgets[widx].pm);
if (afg->widgets[widx].num_inputs) {
int idx;
off = 0;
if (afg->widgets[widx].num_inputs > 1) {
verbs[0] = MAKE_VERB(afg->codec->addr, wid, VID_GET_CONNSEL,
0);
if (hda_send_verbs(afg->codec, verbs, resp, 1) == B_OK)
afg->widgets[widx].active_input = resp[0] & 0xFF;
else
afg->widgets[widx].active_input = -1;
} else
afg->widgets[widx].active_input = -1;
for (idx = 0; idx < afg->widgets[widx].num_inputs; idx ++) {
if (!(idx % 4)) {
verbs[0] = MAKE_VERB(afg->codec->addr, wid,
VID_GET_CONNLENTRY, idx);
if (hda_send_verbs(afg->codec, verbs, resp, 1) != B_OK) {
dprintf("%s: Error parsing inputs for widget %ld!\n",
__func__, wid);
break;
}
}
if (idx != afg->widgets[widx].active_input) {
off += sprintf(buf + off, "%ld ",
(resp[0] >> (8*(idx%4))) & 0xFF);
} else {
off += sprintf(buf + off, "(%ld) ",
(resp[0] >> (8*(idx%4))) & 0xFF);
}
afg->widgets[widx].inputs[idx] = (resp[0] >> (8*(idx%4)))
& 0xFF;
} }
dprintf("\t[ %s]\n", buf); verbs[0] = MAKE_VERB(audioGroup->codec->addr, wid,
VID_GET_CFGDEFAULT, 0);
if (hda_send_verbs(audioGroup->codec, verbs, resp, 1) == B_OK) {
audioGroup->widgets[widx].d.pin.device = (resp[0] >> 20) & 0xF;
dprintf("\t%s, %s, %s, %s\n",
kPortConnector[resp[0] >> 30],
kDefaultDevice[audioGroup->widgets[widx].d.pin.device],
kConnectionType[(resp[0] >> 16) & 0xF],
kJackColor[(resp[0] >> 12) & 0xF]);
}
hda_widget_get_amplifier_capabilities(audioGroup->codec, wid);
break;
case WT_POWER:
dprintf("%ld:\tPower\n", wid);
break;
case WT_VOLUME_KNOB:
dprintf("%ld:\tVolume Knob\n", wid);
break;
case WT_BEEP_GENERATOR:
dprintf("%ld:\tBeep Generator\n", wid);
break;
case WT_VENDOR_DEFINED:
dprintf("%ld:\tVendor Defined\n", wid);
break;
default: /* Reserved */
break;
}
dprintf("\t%s\n", buf);
hda_widget_get_pm_support(audioGroup->codec, wid,
&audioGroup->widgets[widx].pm);
if (audioGroup->widgets[widx].num_inputs) {
int idx;
off = 0;
if (audioGroup->widgets[widx].num_inputs > 1) {
verbs[0] = MAKE_VERB(audioGroup->codec->addr, wid,
VID_GET_CONNSEL, 0);
if (hda_send_verbs(audioGroup->codec, verbs, resp, 1) == B_OK)
audioGroup->widgets[widx].active_input = resp[0] & 0xFF;
else
audioGroup->widgets[widx].active_input = -1;
} else
audioGroup->widgets[widx].active_input = -1;
for (idx = 0; idx < audioGroup->widgets[widx].num_inputs; idx ++) {
if (!(idx % 4)) {
verbs[0] = MAKE_VERB(audioGroup->codec->addr, wid,
VID_GET_CONNLENTRY, idx);
if (hda_send_verbs(audioGroup->codec, verbs, resp, 1) != B_OK) {
dprintf("%s: Error parsing inputs for widget %ld!\n",
__func__, wid);
break;
}
}
if (idx != audioGroup->widgets[widx].active_input) {
off += sprintf(buf + off, "%ld ",
(resp[0] >> (8 * (idx % 4))) & 0xff);
} else {
off += sprintf(buf + off, "(%ld) ",
(resp[0] >> (8 * (idx % 4))) & 0xff);
}
audioGroup->widgets[widx].inputs[idx]
= (resp[0] >> (8 * (idx % 4))) & 0xff;
} }
dprintf("\t[ %s]\n", buf);
} }
} }
@@ -343,38 +353,42 @@ hda_codec_parse_afg(hda_afg* afg)
* 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_audio_group_find_path(hda_audio_group* audioGroup, uint32 widget,
uint32 widgetType, uint32 depth)
{ {
int widx = wid - afg->wid_start; int widx = widget - audioGroup->widget_start;
int idx; int idx;
switch (afg->widgets[widx].type) { switch (audioGroup->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 < audioGroup->widgets[widx].num_inputs; idx++) {
if (hda_codec_afg_find_path(afg, afg->widgets[widx].inputs[idx], wtype, depth +1)) { if (hda_codec_audio_group_find_path(audioGroup,
if (afg->widgets[widx].active_input == -1) audioGroup->widgets[widx].inputs[idx], widgetType,
afg->widgets[widx].active_input = idx; depth + 1)) {
if (audioGroup->widgets[widx].active_input == -1)
audioGroup->widgets[widx].active_input = idx;
return afg->widgets[widx].inputs[idx]; return audioGroup->widgets[widx].inputs[idx];
} }
} }
break; break;
case WT_AUDIO_SELECTOR: case WT_AUDIO_SELECTOR:
{ {
int idx = afg->widgets[widx].active_input; int idx = audioGroup->widgets[widx].active_input;
if (idx != -1) { if (idx != -1) {
uint32 wid = afg->widgets[widx].inputs[idx]; widget = audioGroup->widgets[widx].inputs[idx];
if (hda_codec_afg_find_path(afg, wid, wtype, depth + 1)) { if (hda_codec_audio_group_find_path(audioGroup, widget,
return wid; widgetType, depth + 1)) {
return widget;
} }
} }
break; break;
} }
default: default:
if (afg->widgets[widx].type == wtype) if (audioGroup->widgets[widx].type == widgetType)
return wid; return widget;
break; break;
} }
@@ -384,69 +398,68 @@ hda_codec_afg_find_path(hda_afg* afg, uint32 wid, uint32 wtype, uint32 depth)
static void static void
hda_afg_delete(hda_afg* afg) hda_codec_delete_audio_group(hda_audio_group* audioGroup)
{ {
if (afg != NULL) { if (audioGroup == NULL)
if (afg->playback_stream != NULL) return;
hda_stream_delete(afg->playback_stream);
if (afg->record_stream != NULL) if (audioGroup->playback_stream != NULL)
hda_stream_delete(afg->record_stream); hda_stream_delete(audioGroup->playback_stream);
if (afg->widgets) if (audioGroup->record_stream != NULL)
free(afg->widgets); hda_stream_delete(audioGroup->record_stream);
free(afg); free(audioGroup->widgets);
} free(audioGroup);
} }
static status_t static status_t
hda_codec_afg_new(hda_codec* codec, uint32 afg_nid) hda_codec_new_audio_group(hda_codec* codec, uint32 audioGroupNodeID)
{ {
hda_afg* afg; hda_audio_group* audioGroup;
status_t rc; status_t rc;
uint32 idx; uint32 idx;
if ((afg = calloc(1, sizeof(hda_afg))) == NULL) { if ((audioGroup = calloc(1, sizeof(hda_audio_group))) == NULL) {
rc = B_NO_MEMORY; rc = B_NO_MEMORY;
goto done; goto done;
} }
/* Setup minimal info needed by hda_codec_parse_afg */ /* Setup minimal info needed by hda_codec_parse_afg */
afg->root_nid = afg_nid; audioGroup->root_node_id = audioGroupNodeID;
afg->codec = codec; audioGroup->codec = codec;
/* Parse all widgets in Audio Function Group */ /* Parse all widgets in Audio Function Group */
rc = hda_codec_parse_afg(afg); rc = hda_codec_parse_audio_group(audioGroup);
if (rc != B_OK) if (rc != B_OK)
goto free_afg; goto free_audio_group;
/* Setup for worst-case scenario; we cannot find any output Pin Widgets */ /* Setup for worst-case scenario; we cannot find any output Pin Widgets */
rc = ENODEV; 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 < audioGroup->widget_count; idx++) {
uint32 output_wid = 0, input_wid = 0; uint32 outputWidget = 0, inputWidget = 0;
int32 iidx; int32 iidx;
if (afg->playback_stream == NULL if (audioGroup->playback_stream == NULL
&& afg->widgets[idx].type == WT_PIN_COMPLEX && audioGroup->widgets[idx].type == WT_PIN_COMPLEX
&& afg->widgets[idx].d.pin.output) { && audioGroup->widgets[idx].d.pin.output) {
if (afg->widgets[idx].d.pin.device == PIN_DEV_HP_OUT if (audioGroup->widgets[idx].d.pin.device == PIN_DEV_HP_OUT
|| afg->widgets[idx].d.pin.device == PIN_DEV_SPEAKER || audioGroup->widgets[idx].d.pin.device == PIN_DEV_SPEAKER
|| afg->widgets[idx].d.pin.device == PIN_DEV_LINE_OUT) { || audioGroup->widgets[idx].d.pin.device == PIN_DEV_LINE_OUT) {
iidx = afg->widgets[idx].active_input; iidx = audioGroup->widgets[idx].active_input;
if (iidx != -1) { if (iidx != -1) {
output_wid = hda_codec_afg_find_path(afg, outputWidget = hda_codec_audio_group_find_path(audioGroup,
afg->widgets[idx].inputs[iidx], WT_AUDIO_OUTPUT, 0); audioGroup->widgets[idx].inputs[iidx], WT_AUDIO_OUTPUT, 0);
} else { } else {
for (iidx = 0; iidx < afg->widgets[idx].num_inputs; iidx++) { for (iidx = 0; iidx < audioGroup->widgets[idx].num_inputs; iidx++) {
output_wid = hda_codec_afg_find_path(afg, outputWidget = hda_codec_audio_group_find_path(audioGroup,
afg->widgets[idx].inputs[iidx], WT_AUDIO_OUTPUT, 0); audioGroup->widgets[idx].inputs[iidx], WT_AUDIO_OUTPUT, 0);
if (output_wid) { if (outputWidget) {
corb_t verb = MAKE_VERB(codec->addr, corb_t verb = MAKE_VERB(codec->addr,
idx + afg->wid_start, VID_SET_CONNSEL, iidx); idx + audioGroup->widget_start, VID_SET_CONNSEL, iidx);
if (hda_send_verbs(codec, &verb, NULL, 1) != B_OK) if (hda_send_verbs(codec, &verb, NULL, 1) != B_OK)
dprintf("%s: Setting output selector failed!\n", __func__); dprintf("%s: Setting output selector failed!\n", __func__);
break; break;
@@ -454,48 +467,50 @@ hda_codec_afg_new(hda_codec* codec, uint32 afg_nid)
} }
} }
if (output_wid) { if (outputWidget) {
if (!afg->playback_stream) { if (!audioGroup->playback_stream) {
corb_t verb[2]; corb_t verb[2];
/* Setup playback/record streams for Multi Audio API */ /* Setup playback/record streams for Multi Audio API */
afg->playback_stream = hda_stream_new( audioGroup->playback_stream = hda_stream_new(
afg->codec->controller, STRM_PLAYBACK); audioGroup->codec->controller, STREAM_PLAYBACK);
afg->record_stream = hda_stream_new( audioGroup->record_stream = hda_stream_new(
afg->codec->controller, STRM_RECORD); audioGroup->codec->controller, STREAM_RECORD);
afg->playback_stream->pin_wid = idx + afg->wid_start; audioGroup->playback_stream->pin_widget = idx
afg->playback_stream->io_wid = output_wid; + audioGroup->widget_start;
audioGroup->playback_stream->io_widget = outputWidget;
/* FIXME: Force Pin Widget to unmute; enable hp/output */ /* FIXME: Force Pin Widget to unmute; enable hp/output */
verb[0] = MAKE_VERB(codec->addr, verb[0] = MAKE_VERB(codec->addr,
afg->playback_stream->pin_wid, VID_SET_AMPGAINMUTE, audioGroup->playback_stream->pin_widget, VID_SET_AMPGAINMUTE,
(1 << 15) | (1 << 13) | (1 << 12)); (1 << 15) | (1 << 13) | (1 << 12));
verb[1] = MAKE_VERB(codec->addr, verb[1] = MAKE_VERB(codec->addr,
afg->playback_stream->pin_wid, VID_SET_PINWCTRL, audioGroup->playback_stream->pin_widget, VID_SET_PINWCTRL,
(1 << 7) | (1 << 6)); (1 << 7) | (1 << 6));
hda_send_verbs(codec, verb, NULL, 2); hda_send_verbs(codec, verb, NULL, 2);
dprintf("%s: Found output PIN (%s) connected to output " dprintf("%s: Found output PIN (%s) connected to output "
"CONV wid:%ld\n", __func__, "CONV wid:%ld\n", __func__,
kDefaultDevice[afg->widgets[idx].d.pin.device], output_wid); kDefaultDevice[audioGroup->widgets[idx].d.pin.device], outputWidget);
} }
} }
} }
} }
if (afg->widgets[idx].type == WT_AUDIO_INPUT) { if (audioGroup->widgets[idx].type == WT_AUDIO_INPUT) {
iidx = afg->widgets[idx].active_input; iidx = audioGroup->widgets[idx].active_input;
if (iidx != -1) { if (iidx != -1) {
input_wid = hda_codec_afg_find_path(afg, inputWidget = hda_codec_audio_group_find_path(audioGroup,
afg->widgets[idx].inputs[iidx], WT_PIN_COMPLEX, 0); audioGroup->widgets[idx].inputs[iidx], WT_PIN_COMPLEX, 0);
} else { } else {
for (iidx = 0; iidx < afg->widgets[idx].num_inputs; iidx++) { for (iidx = 0; iidx < audioGroup->widgets[idx].num_inputs; iidx++) {
input_wid = hda_codec_afg_find_path(afg, inputWidget = hda_codec_audio_group_find_path(audioGroup,
afg->widgets[idx].inputs[iidx], WT_PIN_COMPLEX, 0); audioGroup->widgets[idx].inputs[iidx], WT_PIN_COMPLEX, 0);
if (input_wid) { if (inputWidget) {
corb_t verb = MAKE_VERB(codec->addr, corb_t verb = MAKE_VERB(codec->addr,
idx + afg->wid_start, VID_SET_CONNSEL, iidx); idx + audioGroup->widget_start, VID_SET_CONNSEL,
iidx);
if (hda_send_verbs(codec, &verb, NULL, 1) != B_OK) { if (hda_send_verbs(codec, &verb, NULL, 1) != B_OK) {
dprintf("%s: Setting input selector failed!\n", dprintf("%s: Setting input selector failed!\n",
__func__); __func__);
@@ -505,59 +520,64 @@ hda_codec_afg_new(hda_codec* codec, uint32 afg_nid)
} }
} }
if (input_wid) { if (inputWidget) {
if (!afg->record_stream) { if (!audioGroup->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->controller, audioGroup->record_stream = hda_stream_new(
STRM_RECORD); audioGroup->codec->controller, STREAM_RECORD);
afg->record_stream->pin_wid = input_wid; audioGroup->record_stream->pin_widget = inputWidget;
afg->record_stream->io_wid = idx + afg->wid_start; audioGroup->record_stream->io_widget = idx
+ audioGroup->widget_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,
audioGroup->record_stream->pin_widget,
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 " dprintf("%s: Found input PIN (%s) connected to input CONV "
"wid:%ld\n", __func__, kDefaultDevice[afg->widgets[ "wid:%ld\n", __func__, kDefaultDevice[audioGroup->widgets[
input_wid-afg->wid_start].d.pin.device], inputWidget - audioGroup->widget_start].d.pin.device],
idx + afg->wid_start); idx + audioGroup->widget_start);
} }
} }
} }
/* If we found any valid output channels, we're in the clear */ /* If we found any valid output channels, we're in the clear */
if (afg && afg->playback_stream) { if (audioGroup && audioGroup->playback_stream) {
codec->afgs[codec->num_afgs++] = afg; codec->audio_groups[codec->num_audio_groups++] = audioGroup;
rc = B_OK; rc = B_OK;
goto done; goto done;
} }
free_afg: free_audio_group:
free(afg); free(audioGroup);
done: done:
return rc; return rc;
} }
// #pragma mark -
void void
hda_codec_delete(hda_codec* codec) hda_codec_delete(hda_codec* codec)
{ {
if (codec != NULL) { if (codec == NULL) {
uint32 idx; uint32 idx;
delete_sem(codec->response_sem); delete_sem(codec->response_sem);
for (idx = 0; idx < codec->num_afgs; idx++) { for (idx = 0; idx < codec->num_audio_groups; idx++) {
hda_afg_delete(codec->afgs[idx]); hda_codec_delete_audio_group(codec->audio_groups[idx]);
codec->afgs[idx] = NULL; codec->audio_groups[idx] = NULL;
} }
free(codec); free(codec);
} }
} }
@@ -569,7 +589,7 @@ 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];
uint32 nid; uint32 nodeID;
if (codec == NULL) if (codec == NULL)
goto exit_new; goto exit_new;
@@ -587,19 +607,19 @@ hda_codec_new(hda_controller* controller, uint32 cad)
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 (nodeID = responses[2] >> 16;
nid < (responses[2] >> 16) + (responses[2] & 0xFF); nid++) { nodeID < (responses[2] >> 16) + (responses[2] & 0xff); nodeID++) {
uint32 resp; uint32 resp;
verbs[0] = MAKE_VERB(cad, nid, VID_GET_PARAM, PID_FUNCGRP_TYPE); verbs[0] = MAKE_VERB(cad, nodeID, VID_GET_PARAM, PID_FUNCGRP_TYPE);
if (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! */
status_t rc = hda_codec_afg_new(codec, nid); status_t rc = hda_codec_new_audio_group(codec, nodeID);
if (rc != B_OK) { 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));
@@ -134,4 +134,16 @@ typedef enum {
#define PID_OUTPUT_AMP_CAP 0x12 #define PID_OUTPUT_AMP_CAP 0x12
#define PID_VOLUMEKNOB_CAP 0x13 #define PID_VOLUMEKNOB_CAP 0x13
/* PCM support */
#define PCM_8_BIT (1L << 16)
#define PCM_16_BIT (1L << 17)
#define PCM_20_BIT (1L << 18)
#define PCM_24_BIT (1L << 19)
#define PCM_32_BIT (1L << 20)
/* stream support */
#define STREAM_AC3 0x00000004
#define STREAM_FLOAT 0x00000002
#define STREAM_PCM 0x00000001
#endif /* HDA_CODEC_H */ #endif /* HDA_CODEC_H */
@@ -21,8 +21,8 @@ hda_stream_delete(hda_stream* stream)
if (stream->buffer_area >= B_OK) if (stream->buffer_area >= B_OK)
delete_area(stream->buffer_area); delete_area(stream->buffer_area);
if (stream->bdl_area >= B_OK) if (stream->buffer_descriptors_area >= B_OK)
delete_area(stream->bdl_area); delete_area(stream->buffer_descriptors_area);
free(stream); free(stream);
} }
@@ -36,19 +36,19 @@ hda_stream_new(hda_controller* controller, int type)
return NULL; return NULL;
stream->buffer_area = B_ERROR; stream->buffer_area = B_ERROR;
stream->bdl_area = B_ERROR; stream->buffer_descriptors_area = B_ERROR;
switch (type) { switch (type) {
case STRM_PLAYBACK: case STREAM_PLAYBACK:
stream->buffer_ready_sem = create_sem(0, "hda_playback_sem"); stream->buffer_ready_sem = create_sem(0, "hda_playback_sem");
stream->id = 1; stream->id = 1;
stream->off = (controller->num_input_streams * HDAC_SDSIZE); stream->off = (controller->num_input_streams * HDAC_SDSIZE);
controller->streams[controller->num_input_streams] = stream; controller->streams[controller->num_input_streams] = stream;
break; break;
case STRM_RECORD: case STREAM_RECORD:
stream->buffer_area = B_ERROR; stream->buffer_area = B_ERROR;
stream->bdl_area = B_ERROR; stream->buffer_descriptors_area = B_ERROR;
stream->buffer_ready_sem = create_sem(0, "hda_record_sem"); stream->buffer_ready_sem = create_sem(0, "hda_record_sem");
stream->id = 2; stream->id = 2;
stream->off = 0; stream->off = 0;
@@ -80,31 +80,35 @@ hda_stream_start(hda_controller* controller, hda_stream* stream)
} }
status_t //! Called with interrupts off
static void
hda_stream_check_intr(hda_controller* controller, hda_stream* stream) hda_stream_check_intr(hda_controller* controller, hda_stream* stream)
{ {
if (stream->running) { uint8 status;
uint8 sts = OREG8(controller, stream->off, STS);
if (sts) {
cpu_status status;
OREG8(controller, stream->off, STS) = sts; if (!stream->running)
status = disable_interrupts(); return;
acquire_spinlock(&stream->lock);
stream->real_time = system_time(); status = OREG8(controller, stream->off, STS);
stream->frames_count += stream->buffer_length; if (status == 0)
stream->buffer_cycle = (stream->buffer_cycle + 1) return;
% stream->num_buffers;
release_spinlock(&stream->lock); OREG8(controller, stream->off, STS) = status;
restore_interrupts(status);
release_sem_etc(stream->buffer_ready_sem, 1, B_DO_NOT_RESCHEDULE); if ((status & STS_BCIS) != 0) {
} // Buffer Completed Interrupt
} acquire_spinlock(&stream->lock);
return B_OK; stream->real_time = system_time();
stream->frames_count += stream->buffer_length;
stream->buffer_cycle = (stream->buffer_cycle + 1)
% stream->num_buffers;
release_spinlock(&stream->lock);
release_sem_etc(stream->buffer_ready_sem, 1, B_DO_NOT_RESCHEDULE);
} else
dprintf("HDA: stream status %x\n", status);
} }
@@ -123,12 +127,13 @@ hda_stream_stop(hda_controller* controller, hda_stream* stream)
status_t status_t
hda_stream_setup_buffers(hda_afg* afg, hda_stream* stream, const char* desc) hda_stream_setup_buffers(hda_audio_group* audioGroup, hda_stream* stream,
const char* desc)
{ {
uint32 bufferSize, bufferPhysicalAddress, alloc; uint32 bufferSize, bufferPhysicalAddress, alloc;
uint32 response[2], index; uint32 response[2], index;
physical_entry pe; physical_entry pe;
bdl_entry_t* bdl; bdl_entry_t* bufferDescriptors;
corb_t verb[2]; corb_t verb[2];
uint8* buffer; uint8* buffer;
status_t rc; status_t rc;
@@ -140,22 +145,24 @@ hda_stream_setup_buffers(hda_afg* afg, hda_stream* stream, const char* desc)
stream->buffer_area = B_ERROR; stream->buffer_area = B_ERROR;
} }
if (stream->bdl_area >= B_OK) { if (stream->buffer_descriptors_area >= B_OK) {
delete_area(stream->bdl_area); delete_area(stream->buffer_descriptors_area);
stream->bdl_area = B_ERROR; stream->buffer_descriptors_area = B_ERROR;
} }
/* Calculate size of buffer (aligned to 128 bytes) */ /* Calculate size of buffer (aligned to 128 bytes) */
bufferSize = stream->sample_size * stream->num_channels bufferSize = stream->sample_size * stream->num_channels
* stream->buffer_length; * stream->buffer_length;
bufferSize = (bufferSize + 127) & (~127); bufferSize = (bufferSize + 127) & (~127);
dprintf("HDA: sample size %ld, num channels %ld, buffer length %ld ****************\n",
stream->sample_size, stream->num_channels, stream->buffer_length);
/* 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 = bufferSize * stream->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 */
stream->buffer_area = create_area("hda_buffers", (void**)&buffer, stream->buffer_area = create_area("hda buffers", (void**)&buffer,
B_ANY_KERNEL_ADDRESS, alloc, B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA); B_ANY_KERNEL_ADDRESS, alloc, B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA);
if (stream->buffer_area < B_OK) if (stream->buffer_area < B_OK)
return stream->buffer_area; return stream->buffer_area;
@@ -175,55 +182,58 @@ hda_stream_setup_buffers(hda_afg* afg, hda_stream* stream, const char* desc)
/* Store pointers (both virtual/physical) */ /* Store pointers (both virtual/physical) */
for (index = 0; index < stream->num_buffers; index++) { for (index = 0; index < stream->num_buffers; index++) {
stream->buffers[index] = buffer + (index * bufferSize); stream->buffers[index] = buffer + (index * bufferSize);
stream->buffers_pa[index] = bufferPhysicalAddress + (index * bufferSize); stream->physical_buffers[index] = bufferPhysicalAddress
+ (index * bufferSize);
} }
/* Now allocate BDL for buffer range */ /* Now allocate BDL for buffer range */
alloc = stream->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);
stream->bdl_area = create_area("hda_bdl", (void**)&bdl, stream->buffer_descriptors_area = create_area("hda buffer descriptors",
B_ANY_KERNEL_ADDRESS, alloc, B_CONTIGUOUS, 0); (void**)&bufferDescriptors, B_ANY_KERNEL_ADDRESS, alloc,
if (stream->bdl_area < B_OK) { B_CONTIGUOUS, 0);
if (stream->buffer_descriptors_area < B_OK) {
delete_area(stream->buffer_area); delete_area(stream->buffer_area);
return stream->bdl_area; return stream->buffer_descriptors_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(bufferDescriptors, alloc, &pe, 1);
if (rc != B_OK) { if (rc != B_OK) {
delete_area(stream->buffer_area); delete_area(stream->buffer_area);
delete_area(stream->bdl_area); delete_area(stream->buffer_descriptors_area);
return rc; return rc;
} }
stream->bdl_pa = (uint32)pe.address; stream->physical_buffer_descriptors = (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, stream->num_buffers); alloc, stream->num_buffers);
/* Setup BDL entries */ /* Setup BDL entries */
for (index = 0; index < stream->num_buffers; index++, bdl++) { for (index = 0; index < stream->num_buffers; index++, bufferDescriptors++) {
bdl->address = stream->buffers_pa[index]; bufferDescriptors->address = stream->physical_buffers[index];
bdl->length = bufferSize; bufferDescriptors->length = bufferSize;
bdl->ioc = 1; bufferDescriptors->ioc = 1;
} }
/* Configure stream registers */ /* Configure stream registers */
wfmt = stream->num_channels -1; wfmt = stream->num_channels - 1;
switch (stream->sampleformat) { switch (stream->sample_format) {
case B_FMT_8BIT_S: wfmt |= (0 << 4); stream->bps = 8; break; case B_FMT_8BIT_S: wfmt |= (0 << 4); stream->bps = 8; break;
case B_FMT_16BIT: wfmt |= (1 << 4); stream->bps = 16; break; case B_FMT_16BIT: wfmt |= (1 << 4); stream->bps = 16; break;
case B_FMT_20BIT: wfmt |= (2 << 4); stream->bps = 20; break;
case B_FMT_24BIT: wfmt |= (3 << 4); stream->bps = 24; break; case B_FMT_24BIT: wfmt |= (3 << 4); stream->bps = 24; break;
case B_FMT_32BIT: wfmt |= (4 << 4); stream->bps = 32; break; case B_FMT_32BIT: wfmt |= (4 << 4); stream->bps = 32; break;
default: default:
dprintf("%s: Invalid sample format: 0x%lx\n", __func__, dprintf("%s: Invalid sample format: 0x%lx\n", __func__,
stream->sampleformat); stream->sample_format);
break; break;
} }
switch (stream->samplerate) { switch (stream->sample_rate) {
case B_SR_8000: case B_SR_8000:
wfmt |= (0 << 14) | (0 << 11) | (5 << 8); wfmt |= (0 << 14) | (0 << 11) | (5 << 8);
stream->rate = 8000; stream->rate = 8000;
@@ -271,29 +281,32 @@ hda_stream_setup_buffers(hda_afg* afg, hda_stream* stream, const char* desc)
default: default:
dprintf("%s: Invalid sample rate: 0x%lx\n", __func__, dprintf("%s: Invalid sample rate: 0x%lx\n", __func__,
stream->samplerate); stream->sample_rate);
break; break;
} }
dprintf("IRA: %s: setup stream %ld: SR=%ld, SF=%ld\n", __func__, stream->id, dprintf("IRA: %s: setup stream %ld: SR=%ld, SF=%ld\n", __func__, stream->id,
stream->rate, stream->bps); stream->rate, stream->bps);
OREG16(afg->codec->controller, stream->off, FMT) = wfmt; OREG16(audioGroup->codec->controller, stream->off, FMT) = wfmt;
OREG32(afg->codec->controller, stream->off, BDPL) = stream->bdl_pa; OREG32(audioGroup->codec->controller, stream->off, BDPL)
OREG32(afg->codec->controller, stream->off, BDPU) = 0; = stream->physical_buffer_descriptors;
OREG16(afg->codec->controller, stream->off, LVI) = stream->num_buffers -1; OREG32(audioGroup->codec->controller, stream->off, BDPU) = 0;
OREG16(audioGroup->codec->controller, stream->off, LVI)
= stream->num_buffers - 1;
/* total cyclic buffer size in _bytes_ */ /* total cyclic buffer size in _bytes_ */
OREG32(afg->codec->controller, stream->off, CBL) = stream->sample_size OREG32(audioGroup->codec->controller, stream->off, CBL)
* stream->num_channels * stream->num_buffers * stream->buffer_length; = stream->sample_size * stream->num_channels * stream->num_buffers
OREG8(afg->codec->controller, stream->off, CTL0) * stream->buffer_length;
OREG8(audioGroup->codec->controller, stream->off, CTL0)
= CTL0_IOCE | CTL0_FEIE | CTL0_DEIE; = CTL0_IOCE | CTL0_FEIE | CTL0_DEIE;
OREG8(afg->codec->controller, stream->off, CTL2) = stream->id << 4; OREG8(audioGroup->codec->controller, stream->off, CTL2) = stream->id << 4;
verb[0] = MAKE_VERB(afg->codec->addr, stream->io_wid, VID_SET_CONVFORMAT, verb[0] = MAKE_VERB(audioGroup->codec->addr, stream->io_widget,
wfmt); VID_SET_CONVFORMAT, wfmt);
verb[1] = MAKE_VERB(afg->codec->addr, stream->io_wid, VID_SET_CVTSTRCHN, verb[1] = MAKE_VERB(audioGroup->codec->addr, stream->io_widget,
stream->id << 4); VID_SET_CVTSTRCHN, stream->id << 4);
rc = hda_send_verbs(afg->codec, verb, response, 2); rc = hda_send_verbs(audioGroup->codec, verb, response, 2);
return rc; return rc;
} }
@@ -353,7 +366,7 @@ hda_interrupt_handler(hda_controller* controller)
while (controller->rirbrp <= rirbwp) { while (controller->rirbrp <= rirbwp) {
uint32 resp_ex uint32 resp_ex
= controller->rirb[controller->rirbrp].resp_ex; = controller->rirb[controller->rirbrp].resp_ex;
uint32 cad = resp_ex & HDA_MAXCODECS; uint32 cad = resp_ex & HDA_MAX_CODECS;
hda_codec* codec = controller->codecs[cad]; hda_codec* codec = controller->codecs[cad];
if (resp_ex & RESP_EX_UNSOL) { if (resp_ex & RESP_EX_UNSOL) {
@@ -394,7 +407,7 @@ hda_interrupt_handler(hda_controller* controller)
if (intsts & ~(INTSTS_CIS | INTSTS_GIS)) { if (intsts & ~(INTSTS_CIS | INTSTS_GIS)) {
int index; int index;
for (index = 0; index < HDA_MAXSTREAMS; index++) { for (index = 0; index < HDA_MAX_STREAMS; index++) {
if ((intsts & (1 << index)) != 0) { if ((intsts & (1 << index)) != 0) {
if (controller->streams[index]) if (controller->streams[index])
hda_stream_check_intr(controller, controller->streams[index]); hda_stream_check_intr(controller, controller->streams[index]);
@@ -431,59 +444,59 @@ hda_hw_start(hda_controller* controller)
static status_t static status_t
hda_hw_corb_rirb_init(hda_controller* controller) hda_hw_corb_rirb_init(hda_controller* controller)
{ {
uint32 memsz, rirboff; uint32 memSize, rirbOffset;
uint8 corbsz, rirbsz; uint8 corbSize, rirbSize;
status_t rc = B_OK; status_t rc = B_OK;
physical_entry pe; physical_entry pe;
/* Determine and set size of CORB */ /* Determine and set size of CORB */
corbsz = REG8(controller, CORBSIZE); corbSize = REG8(controller, CORBSIZE);
if (corbsz & CORBSIZE_CAP_256E) { if (corbSize & CORBSIZE_CAP_256E) {
controller->corblen = 256; controller->corb_length = 256;
REG8(controller, CORBSIZE) = CORBSIZE_SZ_256E; REG8(controller, CORBSIZE) = CORBSIZE_SZ_256E;
} else if (corbsz & CORBSIZE_CAP_16E) { } else if (corbSize & CORBSIZE_CAP_16E) {
controller->corblen = 16; controller->corb_length = 16;
REG8(controller, CORBSIZE) = CORBSIZE_SZ_16E; REG8(controller, CORBSIZE) = CORBSIZE_SZ_16E;
} else if (corbsz & CORBSIZE_CAP_2E) { } else if (corbSize & CORBSIZE_CAP_2E) {
controller->corblen = 2; controller->corb_length = 2;
REG8(controller, CORBSIZE) = CORBSIZE_SZ_2E; REG8(controller, CORBSIZE) = CORBSIZE_SZ_2E;
} }
/* Determine and set size of RIRB */ /* Determine and set size of RIRB */
rirbsz = REG8(controller, RIRBSIZE); rirbSize = REG8(controller, RIRBSIZE);
if (rirbsz & RIRBSIZE_CAP_256E) { if (rirbSize & RIRBSIZE_CAP_256E) {
controller->rirblen = 256; controller->rirb_length = 256;
REG8(controller, RIRBSIZE) = RIRBSIZE_SZ_256E; REG8(controller, RIRBSIZE) = RIRBSIZE_SZ_256E;
} else if (rirbsz & RIRBSIZE_CAP_16E) { } else if (rirbSize & RIRBSIZE_CAP_16E) {
controller->rirblen = 16; controller->rirb_length = 16;
REG8(controller, RIRBSIZE) = RIRBSIZE_SZ_16E; REG8(controller, RIRBSIZE) = RIRBSIZE_SZ_16E;
} else if (rirbsz & RIRBSIZE_CAP_2E) { } else if (rirbSize & RIRBSIZE_CAP_2E) {
controller->rirblen = 2; controller->rirb_length = 2;
REG8(controller, 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 = (controller->corblen * sizeof(corb_t) + 0x7f) & ~0x7f; rirbOffset = (controller->corb_length * sizeof(corb_t) + 0x7f) & ~0x7f;
memsz = (rirboff + controller->rirblen * sizeof(rirb_t) + B_PAGE_SIZE - 1) memSize = (rirbOffset + controller->rirb_length * sizeof(rirb_t)
& ~(B_PAGE_SIZE - 1); + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
/* Allocate memory area */ /* Allocate memory area */
controller->rb_area = create_area("hda_corb_rirb", (void**)&controller->corb, controller->rb_area = create_area("hda_corb_rirb", (void**)&controller->corb,
B_ANY_KERNEL_ADDRESS, memsz, B_CONTIGUOUS, 0); B_ANY_KERNEL_ADDRESS, memSize, B_CONTIGUOUS, 0);
if (controller->rb_area < 0) if (controller->rb_area < 0)
return controller->rb_area; return controller->rb_area;
/* Rirb is after corb+aligment */ /* Rirb is after corb+aligment */
controller->rirb = (rirb_t*)(((uint8*)controller->corb) + rirboff); controller->rirb = (rirb_t*)(((uint8*)controller->corb) + rirbOffset);
if ((rc = get_memory_map(controller->corb, memsz, &pe, 1)) != B_OK) { if ((rc = get_memory_map(controller->corb, memSize, &pe, 1)) != B_OK) {
delete_area(controller->rb_area); delete_area(controller->rb_area);
return rc; return rc;
} }
/* Program CORB/RIRB for these locations */ /* Program CORB/RIRB for these locations */
REG32(controller, CORBLBASE) = (uint32)pe.address; REG32(controller, CORBLBASE) = (uint32)pe.address;
REG32(controller, RIRBLBASE) = (uint32)pe.address + rirboff; REG32(controller, RIRBLBASE) = (uint32)pe.address + rirbOffset;
/* Reset CORB read pointer */ /* Reset CORB read pointer */
/* NOTE: See HDA011 for corrected procedure! */ /* NOTE: See HDA011 for corrected procedure! */
@@ -574,13 +587,14 @@ hda_hw_init(hda_controller* controller)
goto corb_rirb_failed; goto corb_rirb_failed;
} }
for (index = 0; index < HDA_MAXCODECS; index++) { for (index = 0; index < HDA_MAX_CODECS; index++) {
if ((controller->codecsts & (1 << index)) != 0) if ((controller->codecsts & (1 << index)) != 0)
hda_codec_new(controller, index); hda_codec_new(controller, index);
} }
for (index = 0; index < HDA_MAXCODECS; index++) { for (index = 0; index < HDA_MAX_CODECS; index++) {
if (controller->codecs[index] && controller->codecs[index]->num_afgs) { if (controller->codecs[index]
&& controller->codecs[index]->num_audio_groups > 0) {
controller->active_codec = controller->codecs[index]; controller->active_codec = controller->codecs[index];
break; break;
} }
@@ -617,7 +631,7 @@ hda_hw_stop(hda_controller* controller)
int index; int index;
/* Stop all audio streams */ /* Stop all audio streams */
for (index = 0; index < HDA_MAXSTREAMS; index++) for (index = 0; index < HDA_MAX_STREAMS; index++)
if (controller->streams[index] && controller->streams[index]->running) if (controller->streams[index] && controller->streams[index]->running)
hda_stream_stop(controller, controller->streams[index]); hda_stream_stop(controller, controller->streams[index]);
} }
@@ -661,7 +675,7 @@ hda_hw_uninit(hda_controller* controller)
} }
/* Now delete all codecs */ /* Now delete all codecs */
for (index = 0; index < HDA_MAXCODECS; index++) { for (index = 0; index < HDA_MAX_CODECS; index++) {
if (controller->codecs[index] != NULL) if (controller->codecs[index] != NULL)
hda_codec_delete(controller->codecs[index]); hda_codec_delete(controller->codecs[index]);
} }
@@ -11,15 +11,30 @@
#include "driver.h" #include "driver.h"
#ifdef TRACE
# undef TRACE
#endif
#ifdef TRACE_MULTI_AUDIO
# define TRACE(a...) dprintf("\33[34mhda:\33[0m " a)
#else
# define TRACE(a...) ;
#endif
static multi_channel_info sChannels[] = { 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 },
{ 3, B_MULTI_INPUT_CHANNEL, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 }, { 3, B_MULTI_INPUT_CHANNEL, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 },
{ 4, B_MULTI_OUTPUT_BUS, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO }, { 4, B_MULTI_OUTPUT_BUS, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS,
{ 5, B_MULTI_OUTPUT_BUS, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO }, B_CHANNEL_MINI_JACK_STEREO },
{ 6, B_MULTI_INPUT_BUS, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO }, { 5, B_MULTI_OUTPUT_BUS, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS,
{ 7, B_MULTI_INPUT_BUS, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO }, B_CHANNEL_MINI_JACK_STEREO },
{ 6, B_MULTI_INPUT_BUS, B_CHANNEL_LEFT | 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 },
}; };
@@ -34,10 +49,8 @@ format2size(uint32 format)
case B_FMT_18BIT: case B_FMT_18BIT:
case B_FMT_24BIT: case B_FMT_24BIT:
case B_FMT_32BIT: case B_FMT_32BIT:
return 4;
case B_FMT_FLOAT: case B_FMT_FLOAT:
return 8; return 4;
default: default:
return -1; return -1;
@@ -46,7 +59,7 @@ format2size(uint32 format)
static status_t static status_t
get_description(hda_afg* afg, multi_description* data) get_description(hda_audio_group* audioGroup, 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;
@@ -69,8 +82,8 @@ get_description(hda_afg* afg, multi_description* data)
} }
/* determine output/input rates */ /* determine output/input rates */
data->output_rates = afg->defrates; data->output_rates = audioGroup->supported_rates;
data->input_rates = afg->defrates; data->input_rates = audioGroup->supported_rates;
/* force existance of 48kHz if variable rates are not supported */ /* force existance of 48kHz if variable rates are not supported */
if (data->output_rates == 0) if (data->output_rates == 0)
@@ -81,8 +94,8 @@ get_description(hda_afg* afg, multi_description* data)
data->max_cvsr_rate = 0; data->max_cvsr_rate = 0;
data->min_cvsr_rate = 0; data->min_cvsr_rate = 0;
data->output_formats = afg->deffmts; data->output_formats = audioGroup->supported_formats;
data->input_formats = afg->deffmts; data->input_formats = audioGroup->supported_formats;
data->lock_sources = B_MULTI_LOCK_INTERNAL; data->lock_sources = B_MULTI_LOCK_INTERNAL;
data->timecode_sources = 0; data->timecode_sources = 0;
data->interface_flags = B_MULTI_INTERFACE_PLAYBACK /* | B_MULTI_INTERFACE_RECORD */; data->interface_flags = B_MULTI_INTERFACE_PLAYBACK /* | B_MULTI_INTERFACE_RECORD */;
@@ -95,7 +108,7 @@ get_description(hda_afg* afg, multi_description* data)
static status_t static status_t
get_enabled_channels(hda_afg* afg, multi_channel_enable* data) get_enabled_channels(hda_audio_group* audioGroup, multi_channel_enable* data)
{ {
B_SET_CHANNEL(data->enable_bits, 0, true); B_SET_CHANNEL(data->enable_bits, 0, true);
B_SET_CHANNEL(data->enable_bits, 1, true); B_SET_CHANNEL(data->enable_bits, 1, true);
@@ -108,39 +121,41 @@ get_enabled_channels(hda_afg* afg, multi_channel_enable* data)
static status_t static status_t
get_global_format(hda_afg* afg, multi_format_info* data) get_global_format(hda_audio_group* audioGroup, multi_format_info* data)
{ {
data->output_latency = 0; data->output_latency = 0;
data->input_latency = 0; data->input_latency = 0;
data->timecode_kind = 0; data->timecode_kind = 0;
data->output.format = afg->playback_stream->sampleformat; data->output.format = audioGroup->playback_stream->sample_format;
data->output.rate = afg->playback_stream->samplerate; data->output.rate = audioGroup->playback_stream->sample_rate;
data->input.format = afg->record_stream->sampleformat; data->input.format = audioGroup->record_stream->sample_format;
data->input.rate = afg->record_stream->sampleformat; data->input.rate = audioGroup->record_stream->sample_format;
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_audio_group* audioGroup, multi_format_info* data)
{ {
afg->playback_stream->sampleformat = data->output.format; audioGroup->playback_stream->sample_format = data->output.format;
afg->playback_stream->samplerate = data->output.rate; audioGroup->playback_stream->sample_rate = data->output.rate;
afg->playback_stream->sample_size = format2size(afg->playback_stream->sampleformat); audioGroup->playback_stream->sample_size = format2size(
audioGroup->playback_stream->sample_format);
afg->record_stream->samplerate = data->input.rate; audioGroup->record_stream->sample_rate = data->input.rate;
afg->record_stream->sampleformat = data->input.format; audioGroup->record_stream->sample_format = data->input.format;
afg->record_stream->sample_size = format2size(afg->record_stream->sampleformat); audioGroup->record_stream->sample_size = format2size(
audioGroup->record_stream->sample_format);
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_audio_group* audioGroup, multi_mix_control_info* data)
{ {
data->control_count = 0; data->control_count = 0;
return B_OK; return B_OK;
@@ -148,7 +163,8 @@ list_mix_controls(hda_afg* afg, multi_mix_control_info * data)
static status_t static status_t
list_mix_connections(hda_afg* afg, multi_mix_connection_info * data) list_mix_connections(hda_audio_group* audioGroup,
multi_mix_connection_info* data)
{ {
data->actual_count = 0; data->actual_count = 0;
return B_OK; return B_OK;
@@ -156,89 +172,102 @@ list_mix_connections(hda_afg* afg, multi_mix_connection_info * data)
static status_t static status_t
list_mix_channels(hda_afg* afg, multi_mix_channel_info *data) list_mix_channels(hda_audio_group* audioGroup, 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_audio_group* audioGroup, multi_buffer_list* data)
{ {
uint32 playback_sample_size = afg->playback_stream->sample_size; uint32 playback_sample_size = audioGroup->playback_stream->sample_size;
uint32 record_sample_size = afg->record_stream->sample_size; uint32 record_sample_size = audioGroup->record_stream->sample_size;
uint32 cidx, bidx; uint32 cidx, bidx;
status_t rc; status_t status;
dprintf("%s: playback: %ld buffers, %ld channels, %ld samples\n", __func__, TRACE("playback: %ld buffers, %ld channels, %ld samples\n",
data->request_playback_buffers, data->request_playback_channels, data->request_playback_buffer_size); data->request_playback_buffers, data->request_playback_channels,
dprintf("%s: record: %ld buffers, %ld channels, %ld samples\n", __func__, data->request_playback_buffer_size);
data->request_record_buffers, data->request_record_channels, data->request_record_buffer_size); TRACE("record: %ld buffers, %ld channels, %ld samples\n",
data->request_record_buffers, data->request_record_channels,
data->request_record_buffer_size);
/* Workaround for Haiku multi_audio API, since it prefers to let the driver pick /* Determine what buffers we return given the request */
values, while the BeOS multi_audio actually gives the user's defaults. */
if (data->request_playback_buffers > STRMAXBUF ||
data->request_playback_buffers < STRMINBUF) {
data->request_playback_buffers = STRMINBUF;
}
if (data->request_record_buffers > STRMAXBUF ||
data->request_record_buffers < STRMINBUF) {
data->request_record_buffers = STRMINBUF;
}
if (data->request_playback_buffer_size == 0)
data->request_playback_buffer_size = DEFAULT_FRAMESPERBUF;
if (data->request_record_buffer_size == 0)
data->request_record_buffer_size = DEFAULT_FRAMESPERBUF;
/* ... from here on, we can assume again that a reasonable request is being made */
data->flags = B_MULTI_BUFFER_PLAYBACK;
/* Copy the requested settings into the streams */
afg->playback_stream->num_buffers = data->request_playback_buffers;
afg->playback_stream->num_channels = data->request_playback_channels;
afg->playback_stream->buffer_length = data->request_playback_buffer_size;
if ((rc=hda_stream_setup_buffers(afg, afg->playback_stream, "Playback")) != B_OK) {
dprintf("%s: Error setting up playback buffers (%s)\n", __func__, strerror(rc));
return rc;
}
afg->record_stream->num_buffers = data->request_record_buffers;
afg->record_stream->num_channels = data->request_record_channels;
afg->record_stream->buffer_length = data->request_record_buffer_size;
if ((rc=hda_stream_setup_buffers(afg, afg->record_stream, "Recording")) != B_OK) {
dprintf("%s: Error setting up recording buffers (%s)\n", __func__, strerror(rc));
return rc;
}
/* 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; data->return_playback_buffer_size = data->request_playback_buffer_size;
/* frames */ data->return_record_buffers = data->request_record_buffers;
data->return_record_channels = data->request_record_channels;
data->return_record_buffer_size = data->request_record_buffer_size;
/* Workaround for Haiku multi_audio API, since it prefers to let the
driver pick values, while the BeOS multi_audio actually gives the
user's defaults. */
if (data->return_playback_buffers > STREAM_MAX_BUFFERS
|| data->return_playback_buffers < STREAM_MIN_BUFFERS)
data->return_playback_buffers = STREAM_MIN_BUFFERS;
if (data->return_record_buffers > STREAM_MAX_BUFFERS
|| data->return_record_buffers < STREAM_MIN_BUFFERS)
data->return_record_buffers = STREAM_MIN_BUFFERS;
if (data->return_playback_buffer_size == 0)
data->return_playback_buffer_size = DEFAULT_FRAMES_PER_BUFFER;
if (data->return_record_buffer_size == 0)
data->return_record_buffer_size = DEFAULT_FRAMES_PER_BUFFER;
/* ... from here on, we can assume again that a reasonable request is
being made */
data->flags = B_MULTI_BUFFER_PLAYBACK;
/* Copy the settings into the streams */
audioGroup->playback_stream->num_buffers = data->return_playback_buffers;
audioGroup->playback_stream->num_channels = data->return_playback_channels;
audioGroup->playback_stream->buffer_length
= data->return_playback_buffer_size;
status = hda_stream_setup_buffers(audioGroup, audioGroup->playback_stream,
"Playback");
if (status != B_OK) {
dprintf("hda: Error setting up playback buffers: %s\n",
strerror(status));
return status;
}
audioGroup->record_stream->num_buffers = data->return_record_buffers;
audioGroup->record_stream->num_channels = data->return_record_channels;
audioGroup->record_stream->buffer_length
= data->return_record_buffer_size;
status = hda_stream_setup_buffers(audioGroup, audioGroup->record_stream,
"Recording");
if (status != B_OK) {
dprintf("hda: Error setting up recording buffers: %s\n",
strerror(status));
return status;
}
/* Setup data structure for multi_audio API... */
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 data->playback_buffers[bidx][cidx].base
= afg->playback_stream->buffers[bidx] = audioGroup->playback_stream->buffers[bidx]
+ (playback_sample_size * cidx); + (playback_sample_size * cidx);
data->playback_buffers[bidx][cidx].stride data->playback_buffers[bidx][cidx].stride
= playback_sample_size * data->return_playback_channels; = playback_sample_size * data->return_playback_channels;
} }
} }
data->return_record_buffers = data->request_record_buffers;
data->return_record_channels = data->request_record_channels;
data->return_record_buffer_size = data->request_record_buffer_size;
/* frames */
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 data->record_buffers[bidx][cidx].base
= afg->record_stream->buffers[bidx] + (record_sample_size * cidx); = audioGroup->record_stream->buffers[bidx]
+ (record_sample_size * cidx);
data->record_buffers[bidx][cidx].stride data->record_buffers[bidx][cidx].stride
= record_sample_size * data->return_record_channels; = record_sample_size * data->return_record_channels;
} }
@@ -247,20 +276,22 @@ get_buffers(hda_afg* afg, multi_buffer_list* data)
return B_OK; return B_OK;
} }
/* playback_buffer_cycle is the buffer we want to have played */
/*! playback_buffer_cycle is the buffer we want to have played */
static status_t static status_t
buffer_exchange(hda_afg* afg, multi_buffer_info* data) buffer_exchange(hda_audio_group* audioGroup, multi_buffer_info* data)
{ {
static int debug_buffers_exchanged = 0; static int debug_buffers_exchanged = 0;
cpu_status status; cpu_status status;
status_t rc; status_t rc;
if (!afg->playback_stream->running) if (!audioGroup->playback_stream->running) {
hda_stream_start(afg->codec->controller, afg->playback_stream); hda_stream_start(audioGroup->codec->controller,
audioGroup->playback_stream);
}
/* do playback */ /* do playback */
rc=acquire_sem(afg->playback_stream->buffer_ready_sem); rc = acquire_sem(audioGroup->playback_stream->buffer_ready_sem);
if (rc != B_OK) { if (rc != B_OK) {
dprintf("%s: Error waiting for playback buffer to finish (%s)!\n", __func__, dprintf("%s: Error waiting for playback buffer to finish (%s)!\n", __func__,
strerror(rc)); strerror(rc));
@@ -268,13 +299,13 @@ buffer_exchange(hda_afg* afg, multi_buffer_info* data)
} }
status = disable_interrupts(); status = disable_interrupts();
acquire_spinlock(&afg->playback_stream->lock); acquire_spinlock(&audioGroup->playback_stream->lock);
data->playback_buffer_cycle = afg->playback_stream->buffer_cycle; data->playback_buffer_cycle = audioGroup->playback_stream->buffer_cycle;
data->played_real_time = afg->playback_stream->real_time; data->played_real_time = audioGroup->playback_stream->real_time;
data->played_frames_count = afg->playback_stream->frames_count; data->played_frames_count = audioGroup->playback_stream->frames_count;
release_spinlock(&afg->playback_stream->lock); release_spinlock(&audioGroup->playback_stream->lock);
restore_interrupts(status); restore_interrupts(status);
debug_buffers_exchanged++; debug_buffers_exchanged++;
@@ -285,57 +316,59 @@ buffer_exchange(hda_afg* afg, multi_buffer_info* data)
return B_OK; return B_OK;
} }
static status_t
buffer_force_stop(hda_afg* afg)
{
hda_stream_stop(afg->codec->controller, afg->playback_stream);
//hda_stream_stop(afg->codec->controller, afg->record_stream);
delete_sem(afg->playback_stream->buffer_ready_sem); static status_t
// delete_sem(afg->record_stream->buffer_ready_sem); buffer_force_stop(hda_audio_group* audioGroup)
{
hda_stream_stop(audioGroup->codec->controller, audioGroup->playback_stream);
//hda_stream_stop(audioGroup->codec->controller, audioGroup->record_stream);
delete_sem(audioGroup->playback_stream->buffer_ready_sem);
// delete_sem(audioGroup->record_stream->buffer_ready_sem);
return B_OK; return B_OK;
} }
status_t status_t
multi_audio_control(void* cookie, uint32 op, void* arg, size_t len) multi_audio_control(void* cookie, uint32 op, void* arg, size_t len)
{ {
hda_codec* codec = (hda_codec*)cookie; hda_codec* codec = (hda_codec*)cookie;
hda_afg* afg; hda_audio_group* audioGroup;
/* FIXME: We should simply pass the afg into here... */ /* FIXME: We should simply pass the audioGroup into here... */
if (!codec || codec->num_afgs == 0) if (!codec || codec->num_audio_groups == 0)
return ENODEV; return ENODEV;
afg = codec->afgs[0]; audioGroup = codec->audio_groups[0];
switch (op) { switch (op) {
case B_MULTI_GET_DESCRIPTION: case B_MULTI_GET_DESCRIPTION:
return get_description(afg, arg); return get_description(audioGroup, arg);
case B_MULTI_GET_ENABLED_CHANNELS: case B_MULTI_GET_ENABLED_CHANNELS:
return get_enabled_channels(afg, arg); return get_enabled_channels(audioGroup, arg);
case B_MULTI_SET_ENABLED_CHANNELS: case B_MULTI_SET_ENABLED_CHANNELS:
return B_OK; return B_OK;
case B_MULTI_GET_GLOBAL_FORMAT: case B_MULTI_GET_GLOBAL_FORMAT:
return get_global_format(afg, arg); return get_global_format(audioGroup, arg);
case B_MULTI_SET_GLOBAL_FORMAT: case B_MULTI_SET_GLOBAL_FORMAT:
return set_global_format(afg, arg); return set_global_format(audioGroup, arg);
case B_MULTI_LIST_MIX_CHANNELS: case B_MULTI_LIST_MIX_CHANNELS:
return list_mix_channels(afg, arg); return list_mix_channels(audioGroup, arg);
case B_MULTI_LIST_MIX_CONTROLS: case B_MULTI_LIST_MIX_CONTROLS:
return list_mix_controls(afg, arg); return list_mix_controls(audioGroup, arg);
case B_MULTI_LIST_MIX_CONNECTIONS: case B_MULTI_LIST_MIX_CONNECTIONS:
return list_mix_connections(afg, arg); return list_mix_connections(audioGroup, arg);
case B_MULTI_GET_BUFFERS: case B_MULTI_GET_BUFFERS:
return get_buffers(afg, arg); return get_buffers(audioGroup, arg);
case B_MULTI_BUFFER_EXCHANGE: case B_MULTI_BUFFER_EXCHANGE:
return buffer_exchange(afg, arg); return buffer_exchange(audioGroup, arg);
case B_MULTI_BUFFER_FORCE_STOP: case B_MULTI_BUFFER_FORCE_STOP:
return buffer_force_stop(afg); return buffer_force_stop(audioGroup);
case B_MULTI_GET_EVENT_INFO: case B_MULTI_GET_EVENT_INFO:
case B_MULTI_SET_EVENT_INFO: case B_MULTI_SET_EVENT_INFO: