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;
hda_controller gCards[MAXCARDS];
hda_controller gCards[MAX_CARDS];
uint32 gNumCards;
pci_module_info* gPci;
@@ -51,7 +51,8 @@ init_driver(void)
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
&& info.class_sub == PCI_hd_audio) {
gCards[gNumCards].pci_info = info;
@@ -93,7 +94,7 @@ uninit_driver(void)
const char**
publish_devices(void)
{
static const char* devs[MAXCARDS+1];
static const char* devs[MAX_CARDS + 1];
long i;
dprintf("IRA: %s\n", __func__);
+95 -96
View File
@@ -8,48 +8,48 @@
#ifndef _HDA_H_
#define _HDA_H_
#include <drivers/KernelExport.h>
#include <drivers/Drivers.h>
#include <drivers/PCI.h>
#include <KernelExport.h>
#include <Drivers.h>
#include <PCI.h>
#include <string.h>
#include <stdlib.h>
#ifndef HAIKU_TARGET_PLATFORM_HAIKU
#define DEVFS_PATH_FORMAT "audio/multi/hda/%lu"
#include <multi_audio.h>
# define DEVFS_PATH_FORMAT "audio/multi/hda/%lu"
# include <multi_audio.h>
#else
#define DEVFS_PATH_FORMAT "audio/hmulti/hda/%lu"
#include <hmulti_audio.h>
# define DEVFS_PATH_FORMAT "audio/hmulti/hda/%lu"
# include <hmulti_audio.h>
#endif
#include "hda_controller_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) */
#define PCI_hd_audio 3
#define PCI_hd_audio 3
#define HDA_MAXAFGS 15
#define HDA_MAXCODECS 15
#define HDA_MAXSTREAMS 16
#define MAX_CODEC_RESPONSES 10
#define MAXINPUTS 32
#define HDA_MAX_AUDIO_GROUPS 15
#define HDA_MAX_CODECS 15
#define HDA_MAX_STREAMS 16
#define MAX_CODEC_RESPONSES 10
#define MAX_INPUTS 32
/* 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_codec_s hda_codec;
typedef struct hda_afg_s hda_afg;
typedef struct hda_controller hda_controller;
typedef struct hda_codec hda_codec;
typedef struct hda_audio_group hda_audio_group;
#define STRMAXBUF 10
#define STRMINBUF 2
#define STREAM_MAX_BUFFERS 10
#define STREAM_MIN_BUFFERS 2
enum {
STRM_PLAYBACK,
STRM_RECORD
STREAM_PLAYBACK,
STREAM_RECORD
};
/* hda_stream_info
@@ -58,24 +58,26 @@ enum {
* which is can have multiple channels (for stereo or better).
*/
typedef struct hda_stream_info_s {
uint32 id; /* HDA controller stream # */
typedef struct hda_stream_info {
uint32 id; /* HDA controller stream # */
uint32 off; /* HDA I/O/B descriptor offset */
bool running; /* Is this stream active? */
spinlock lock; /* Write lock */
uint32 pin_wid; /* PIN Widget ID */
uint32 io_wid; /* Input/Output Converter Widget ID */
uint32 pin_widget; /* PIN Widget ID */
uint32 io_widget; /* Input/Output Converter Widget ID */
uint32 samplerate;
uint32 sampleformat;
uint32 sample_rate;
uint32 sample_format;
uint32 num_buffers;
uint32 num_channels;
uint32 buffer_length; /* size of buffer in samples */
uint32 buffer_length; /* size of buffer in samples */
uint32 sample_size;
void* buffers[STRMAXBUF]; /* Virtual addresses for buffer */
uint32 buffers_pa[STRMAXBUF]; /* Physical addresses for buffer */
void* buffers[STREAM_MAX_BUFFERS];
/* Virtual addresses for buffer */
uint32 physical_buffers[STREAM_MAX_BUFFERS];
/* Physical addresses for buffer */
sem_id buffer_ready_sem;
bigtime_t real_time;
uint32 frames_count;
@@ -84,38 +86,36 @@ typedef struct hda_stream_info_s {
uint32 rate, bps; /* Samplerate & bits per sample */
area_id buffer_area;
area_id bdl_area;
uint32 bdl_pa; /* BDL physical address */
area_id buffer_descriptors_area;
uint32 physical_buffer_descriptors; /* BDL physical address */
} hda_stream;
/* hda_afg
/* hda_audio_group
*
* This structure describes a single Audio Function Group. An afg
* 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)
* or from an input device to the HDA link (= recording).
*/
struct hda_afg_s {
hda_codec* codec;
struct hda_audio_group {
hda_codec* codec;
/* Multi Audio API data */
hda_stream* playback_stream;
hda_stream* record_stream;
hda_stream* playback_stream;
hda_stream* record_stream;
uint32 root_nid,
wid_start,
wid_count;
uint32 deffmts,
defrates,
defpm;
uint32 root_node_id;
uint32 widget_start;
uint32 widget_count;
uint32 supported_formats;
uint32 supported_rates;
uint32 supported_pm;
struct {
uint32 num_inputs;
int32 active_input;
uint32 inputs[MAXINPUTS];
uint32 inputs[MAX_INPUTS];
uint32 flags;
hda_widget_type type;
@@ -133,9 +133,9 @@ struct hda_afg_s {
struct {
} mixer;
struct {
uint32 output;
uint32 input;
pin_dev_type device;
uint32 output;
uint32 input;
pin_dev_type device;
} pin;
} d;
} *widgets;
@@ -150,22 +150,21 @@ struct hda_afg_s {
*
* 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 {
uint16 vendor_id;
uint16 product_id;
uint8 hda_rev;
uint16 rev_stepping;
uint8 addr;
sem_id response_sem;
uint32 responses[MAX_CODEC_RESPONSES];
uint32 response_count;
sem_id response_sem;
uint32 responses[MAX_CODEC_RESPONSES];
uint32 response_count;
hda_audio_group* audio_groups[HDA_MAX_AUDIO_GROUPS];
uint32 num_audio_groups;
hda_afg* afgs[HDA_MAXAFGS];
uint32 num_afgs;
struct hda_controller_s* controller;
struct hda_controller* controller;
};
/* hda_controller
@@ -175,61 +174,61 @@ struct hda_codec_s {
* for use by the codecs contained, and the messaging queue
* (verb/response) buffers for communication.
*/
struct hda_controller_s {
struct hda_controller {
struct pci_info pci_info;
vuint32 opened;
const char* devfs_path;
area_id regs_area;
vuint8* regs;
uint32 irq;
vuint32 opened;
const char* devfs_path;
uint16 codecsts;
uint32 num_input_streams;
uint32 num_output_streams;
uint32 num_bidir_streams;
uint32 corblen;
uint32 rirblen;
uint32 rirbrp;
uint32 corbwp;
area_id rb_area;
corb_t* corb;
rirb_t* rirb;
area_id regs_area;
vuint8* regs;
uint32 irq;
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;
uint32 num_codecs;
hda_stream* streams[HDA_MAXSTREAMS];
hda_stream* streams[HDA_MAX_STREAMS];
};
/* driver.c */
extern device_hooks gDriverHooks;
extern pci_module_info* gPci;
extern hda_controller gCards[MAXCARDS];
extern hda_controller gCards[MAX_CARDS];
extern uint32 gNumCards;
/* hda_codec.c */
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 */
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 */
status_t hda_hw_init(hda_controller* controller);
void hda_hw_stop(hda_controller* controller);
void hda_hw_uninit(hda_controller* controller);
status_t hda_send_verbs(hda_codec* codec, corb_t* verbs, uint32* responses, int count);
status_t hda_send_verbs(hda_codec* codec, corb_t* verbs, uint32* responses,
int count);
/* hda_controller.c: Stream support */
hda_stream* hda_stream_new(hda_controller* controller, int type);
void hda_stream_delete(hda_stream* s);
status_t hda_stream_setup_buffers(hda_afg* afg, hda_stream* s, const char* desc);
status_t hda_stream_start(hda_controller* controller, hda_stream* s);
status_t hda_stream_stop(hda_controller* controller, hda_stream* s);
status_t hda_stream_check_intr(hda_controller* controller, hda_stream* s);
void hda_stream_delete(hda_stream* stream);
status_t hda_stream_setup_buffers(hda_audio_group* audioGroup,
hda_stream* stream, const char* desc);
status_t hda_stream_start(hda_controller* controller, hda_stream* stream);
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
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);
status_t rc;
uint32 resp;
@@ -56,72 +56,77 @@ hda_widget_get_pm_support(hda_codec* codec, uint32 nid, uint32* pm)
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)
{
corb_t verbs[2];
uint32 resp[2];
status_t rc;
status_t status;
verbs[0] = MAKE_VERB(codec->addr,nid,VID_GET_PARAM,PID_STREAM_SUPPORT);
verbs[1] = MAKE_VERB(codec->addr,nid,VID_GET_PARAM,PID_PCM_SUPPORT);
if ((rc = hda_send_verbs(codec, verbs, resp, 2)) == B_OK) {
*fmts = 0; *rates = 0;
verbs[0] = MAKE_VERB(codec->addr, nodeID, VID_GET_PARAM, PID_STREAM_SUPPORT);
verbs[1] = MAKE_VERB(codec->addr, nodeID, VID_GET_PARAM, PID_PCM_SUPPORT);
if (resp[2] & (1 << 0)) {
if (resp[1] & (1 << 0))
*rates |= B_SR_8000;
if (resp[1] & (1 << 1))
*rates |= B_SR_11025;
if (resp[1] & (1 << 2))
*rates |= B_SR_16000;
if (resp[1] & (1 << 3))
*rates |= B_SR_22050;
if (resp[1] & (1 << 4))
*rates |= B_SR_32000;
if (resp[1] & (1 << 5))
*rates |= B_SR_44100;
if (resp[1] & (1 << 6))
*rates |= B_SR_48000;
if (resp[1] & (1 << 7))
*rates |= B_SR_88200;
if (resp[1] & (1 << 8))
*rates |= B_SR_96000;
if (resp[1] & (1 << 9))
*rates |= B_SR_176400;
if (resp[1] & (1 << 10))
*rates |= B_SR_192000;
if (resp[1] & (1 << 11))
*rates |= B_SR_384000;
status = hda_send_verbs(codec, verbs, resp, 2);
if (status != B_OK)
return status;
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;
}
*formats = 0;
*rates = 0;
//FIXME: if (resp[0] & (1 << 1)) *fmts |= B_FMT_FLOAT;
//FIXME: if (resp[0] & (1 << 2)) /* Sort out how to handle AC3 */;
if ((resp[0] & (STREAM_FLOAT | STREAM_PCM)) != 0) {
if (resp[1] & (1 << 0))
*rates |= B_SR_8000;
if (resp[1] & (1 << 1))
*rates |= B_SR_11025;
if (resp[1] & (1 << 2))
*rates |= B_SR_16000;
if (resp[1] & (1 << 3))
*rates |= B_SR_22050;
if (resp[1] & (1 << 4))
*rates |= B_SR_32000;
if (resp[1] & (1 << 5))
*rates |= B_SR_44100;
if (resp[1] & (1 << 6))
*rates |= B_SR_48000;
if (resp[1] & (1 << 7))
*rates |= B_SR_88200;
if (resp[1] & (1 << 8))
*rates |= B_SR_96000;
if (resp[1] & (1 << 9))
*rates |= B_SR_176400;
if (resp[1] & (1 << 10))
*rates |= B_SR_192000;
if (resp[1] & (1 << 11))
*rates |= B_SR_384000;
if (resp[1] & 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
hda_widget_get_amplifier_capabilities(hda_codec* codec, uint32 nid)
hda_widget_get_amplifier_capabilities(hda_codec* codec, uint32 nodeID)
{
status_t rc;
corb_t verb;
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);
if (rc == B_OK && resp != 0) {
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
hda_codec_parse_afg(hda_afg* afg)
hda_codec_parse_audio_group(hda_audio_group* audioGroup)
{
corb_t verbs[6];
uint32 resp[6];
uint32 widx;
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_stream_support(audioGroup->codec, audioGroup->root_node_id,
&audioGroup->supported_formats, &audioGroup->supported_rates);
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,
PID_AUDIO_FG_CAP);
verbs[1] = MAKE_VERB(afg->codec->addr, afg->root_nid, VID_GET_PARAM,
PID_GPIO_COUNT);
verbs[2] = MAKE_VERB(afg->codec->addr, afg->root_nid, VID_GET_PARAM,
PID_SUBORD_NODE_COUNT);
verbs[0] = MAKE_VERB(audioGroup->codec->addr, audioGroup->root_node_id,
VID_GET_PARAM, PID_AUDIO_FG_CAP);
verbs[1] = MAKE_VERB(audioGroup->codec->addr, audioGroup->root_node_id,
VID_GET_PARAM, PID_GPIO_COUNT);
verbs[2] = MAKE_VERB(audioGroup->codec->addr, audioGroup->root_node_id,
VID_GET_PARAM, PID_SUBORD_NODE_COUNT);
if (hda_send_verbs(afg->codec, verbs, resp, 3) == B_OK) {
dprintf("%s: Output delay: %ld samples, Input delay: %ld samples, "
"Beep Generator: %s\n", __func__, resp[0] & 0xf,
(resp[0] >> 8) & 0xf, (resp[0] & (1 << 16)) ? "yes" : "no");
if (hda_send_verbs(audioGroup->codec, verbs, resp, 3) != B_OK)
return B_ERROR;
dprintf("%s: #GPIO: %ld, #GPO: %ld, #GPI: %ld, unsol: %s, wake: %s\n",
__func__, resp[4] & 0xFF, (resp[1] >> 8) & 0xFF,
(resp[1] >> 16) & 0xFF, (resp[1] & (1 << 30)) ? "yes" : "no",
(resp[1] & (1 << 31)) ? "yes" : "no");
dprintf("%s: Output delay: %ld samples, Input delay: %ld samples, "
"Beep Generator: %s\n", __func__, resp[0] & 0xf,
(resp[0] >> 8) & 0xf, (resp[0] & (1 << 16)) ? "yes" : "no");
afg->wid_start = resp[2] >> 16;
afg->wid_count = resp[2] & 0xFF;
dprintf("%s: #GPIO: %ld, #GPO: %ld, #GPI: %ld, unsol: %s, wake: %s\n",
__func__, resp[4] & 0xFF, (resp[1] >> 8) & 0xFF,
(resp[1] >> 16) & 0xFF, (resp[1] & (1 << 30)) ? "yes" : "no",
(resp[1] & (1 << 31)) ? "yes" : "no");
afg->widgets = calloc(afg->wid_count, sizeof(*afg->widgets));
if (afg->widgets == NULL) {
dprintf("ERROR: Not enough memory!\n");
return B_NO_MEMORY;
audioGroup->widget_start = resp[2] >> 16;
audioGroup->widget_count = resp[2] & 0xFF;
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 */
for (widx = 0; widx < afg->wid_count; widx++) {
uint32 wid = afg->wid_start + widx;
char buf[256];
int off;
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] ");
verbs[0] = MAKE_VERB(afg->codec->addr, wid, VID_GET_PARAM,
PID_AUDIO_WIDGET_CAP);
verbs[1] = MAKE_VERB(afg->codec->addr, wid, VID_GET_PARAM,
PID_CONNLIST_LEN);
hda_send_verbs(afg->codec, verbs, resp, 2);
switch (audioGroup->widgets[widx].type) {
case WT_AUDIO_OUTPUT:
dprintf("%ld:\tAudio Output\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_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;
afg->widgets[widx].num_inputs = resp[1] & 0x7F;
off = 0;
if (resp[0] & (1 << 11))
off += sprintf(buf + off, "[L-R Swap] ");
if (resp[0] & (1 << 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%s\n",
audioGroup->widgets[widx].d.pin.input ? "[Input] " : "",
audioGroup->widgets[widx].d.pin.input ? "[Output]" : "");
} else {
dprintf("%s: Error getting Pin Complex IO\n", __func__);
}
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.
*/
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;
switch (afg->widgets[widx].type) {
switch (audioGroup->widgets[widx].type) {
case WT_AUDIO_MIXER:
for (idx = 0; idx < afg->widgets[widx].num_inputs; idx++) {
if (hda_codec_afg_find_path(afg, afg->widgets[widx].inputs[idx], wtype, depth +1)) {
if (afg->widgets[widx].active_input == -1)
afg->widgets[widx].active_input = idx;
for (idx = 0; idx < audioGroup->widgets[widx].num_inputs; idx++) {
if (hda_codec_audio_group_find_path(audioGroup,
audioGroup->widgets[widx].inputs[idx], widgetType,
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;
case WT_AUDIO_SELECTOR:
{
int idx = afg->widgets[widx].active_input;
int idx = audioGroup->widgets[widx].active_input;
if (idx != -1) {
uint32 wid = afg->widgets[widx].inputs[idx];
if (hda_codec_afg_find_path(afg, wid, wtype, depth + 1)) {
return wid;
widget = audioGroup->widgets[widx].inputs[idx];
if (hda_codec_audio_group_find_path(audioGroup, widget,
widgetType, depth + 1)) {
return widget;
}
}
break;
}
default:
if (afg->widgets[widx].type == wtype)
return wid;
if (audioGroup->widgets[widx].type == widgetType)
return widget;
break;
}
@@ -384,69 +398,68 @@ hda_codec_afg_find_path(hda_afg* afg, uint32 wid, uint32 wtype, uint32 depth)
static void
hda_afg_delete(hda_afg* afg)
hda_codec_delete_audio_group(hda_audio_group* audioGroup)
{
if (afg != NULL) {
if (afg->playback_stream != NULL)
hda_stream_delete(afg->playback_stream);
if (audioGroup == NULL)
return;
if (afg->record_stream != NULL)
hda_stream_delete(afg->record_stream);
if (audioGroup->playback_stream != NULL)
hda_stream_delete(audioGroup->playback_stream);
if (afg->widgets)
free(afg->widgets);
free(afg);
}
if (audioGroup->record_stream != NULL)
hda_stream_delete(audioGroup->record_stream);
free(audioGroup->widgets);
free(audioGroup);
}
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;
uint32 idx;
if ((afg = calloc(1, sizeof(hda_afg))) == NULL) {
if ((audioGroup = calloc(1, sizeof(hda_audio_group))) == NULL) {
rc = B_NO_MEMORY;
goto done;
}
/* Setup minimal info needed by hda_codec_parse_afg */
afg->root_nid = afg_nid;
afg->codec = codec;
audioGroup->root_node_id = audioGroupNodeID;
audioGroup->codec = codec;
/* Parse all widgets in Audio Function Group */
rc = hda_codec_parse_afg(afg);
rc = hda_codec_parse_audio_group(audioGroup);
if (rc != B_OK)
goto free_afg;
goto free_audio_group;
/* Setup for worst-case scenario; we cannot find any output Pin Widgets */
rc = ENODEV;
/* Try to locate all input/output channels */
for (idx = 0; idx < afg->wid_count; idx++) {
uint32 output_wid = 0, input_wid = 0;
for (idx = 0; idx < audioGroup->widget_count; idx++) {
uint32 outputWidget = 0, inputWidget = 0;
int32 iidx;
if (afg->playback_stream == NULL
&& afg->widgets[idx].type == WT_PIN_COMPLEX
&& afg->widgets[idx].d.pin.output) {
if (afg->widgets[idx].d.pin.device == PIN_DEV_HP_OUT
|| afg->widgets[idx].d.pin.device == PIN_DEV_SPEAKER
|| afg->widgets[idx].d.pin.device == PIN_DEV_LINE_OUT) {
iidx = afg->widgets[idx].active_input;
if (audioGroup->playback_stream == NULL
&& audioGroup->widgets[idx].type == WT_PIN_COMPLEX
&& audioGroup->widgets[idx].d.pin.output) {
if (audioGroup->widgets[idx].d.pin.device == PIN_DEV_HP_OUT
|| audioGroup->widgets[idx].d.pin.device == PIN_DEV_SPEAKER
|| audioGroup->widgets[idx].d.pin.device == PIN_DEV_LINE_OUT) {
iidx = audioGroup->widgets[idx].active_input;
if (iidx != -1) {
output_wid = hda_codec_afg_find_path(afg,
afg->widgets[idx].inputs[iidx], WT_AUDIO_OUTPUT, 0);
outputWidget = hda_codec_audio_group_find_path(audioGroup,
audioGroup->widgets[idx].inputs[iidx], WT_AUDIO_OUTPUT, 0);
} else {
for (iidx = 0; iidx < afg->widgets[idx].num_inputs; iidx++) {
output_wid = hda_codec_afg_find_path(afg,
afg->widgets[idx].inputs[iidx], WT_AUDIO_OUTPUT, 0);
if (output_wid) {
for (iidx = 0; iidx < audioGroup->widgets[idx].num_inputs; iidx++) {
outputWidget = hda_codec_audio_group_find_path(audioGroup,
audioGroup->widgets[idx].inputs[iidx], WT_AUDIO_OUTPUT, 0);
if (outputWidget) {
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)
dprintf("%s: Setting output selector failed!\n", __func__);
break;
@@ -454,48 +467,50 @@ hda_codec_afg_new(hda_codec* codec, uint32 afg_nid)
}
}
if (output_wid) {
if (!afg->playback_stream) {
if (outputWidget) {
if (!audioGroup->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);
audioGroup->playback_stream = hda_stream_new(
audioGroup->codec->controller, STREAM_PLAYBACK);
audioGroup->record_stream = hda_stream_new(
audioGroup->codec->controller, STREAM_RECORD);
afg->playback_stream->pin_wid = idx + afg->wid_start;
afg->playback_stream->io_wid = output_wid;
audioGroup->playback_stream->pin_widget = idx
+ audioGroup->widget_start;
audioGroup->playback_stream->io_widget = outputWidget;
/* FIXME: Force Pin Widget to unmute; enable hp/output */
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));
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));
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);
kDefaultDevice[audioGroup->widgets[idx].d.pin.device], outputWidget);
}
}
}
}
if (afg->widgets[idx].type == WT_AUDIO_INPUT) {
iidx = afg->widgets[idx].active_input;
if (audioGroup->widgets[idx].type == WT_AUDIO_INPUT) {
iidx = audioGroup->widgets[idx].active_input;
if (iidx != -1) {
input_wid = hda_codec_afg_find_path(afg,
afg->widgets[idx].inputs[iidx], WT_PIN_COMPLEX, 0);
inputWidget = hda_codec_audio_group_find_path(audioGroup,
audioGroup->widgets[idx].inputs[iidx], WT_PIN_COMPLEX, 0);
} else {
for (iidx = 0; iidx < afg->widgets[idx].num_inputs; iidx++) {
input_wid = hda_codec_afg_find_path(afg,
afg->widgets[idx].inputs[iidx], WT_PIN_COMPLEX, 0);
if (input_wid) {
for (iidx = 0; iidx < audioGroup->widgets[idx].num_inputs; iidx++) {
inputWidget = hda_codec_audio_group_find_path(audioGroup,
audioGroup->widgets[idx].inputs[iidx], WT_PIN_COMPLEX, 0);
if (inputWidget) {
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) {
dprintf("%s: Setting input selector failed!\n",
__func__);
@@ -505,59 +520,64 @@ hda_codec_afg_new(hda_codec* codec, uint32 afg_nid)
}
}
if (input_wid) {
if (!afg->record_stream) {
if (inputWidget) {
if (!audioGroup->record_stream) {
corb_t verb;
/* Setup playback/record streams for Multi Audio API */
afg->record_stream = hda_stream_new(afg->codec->controller,
STRM_RECORD);
audioGroup->record_stream = hda_stream_new(
audioGroup->codec->controller, STREAM_RECORD);
afg->record_stream->pin_wid = input_wid;
afg->record_stream->io_wid = idx + afg->wid_start;
audioGroup->record_stream->pin_widget = inputWidget;
audioGroup->record_stream->io_widget = idx
+ audioGroup->widget_start;
/* 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));
hda_send_verbs(codec, &verb, NULL, 1);
}
dprintf("%s: Found input PIN (%s) connected to input CONV "
"wid:%ld\n", __func__, kDefaultDevice[afg->widgets[
input_wid-afg->wid_start].d.pin.device],
idx + afg->wid_start);
"wid:%ld\n", __func__, kDefaultDevice[audioGroup->widgets[
inputWidget - audioGroup->widget_start].d.pin.device],
idx + audioGroup->widget_start);
}
}
}
/* If we found any valid output channels, we're in the clear */
if (afg && afg->playback_stream) {
codec->afgs[codec->num_afgs++] = afg;
if (audioGroup && audioGroup->playback_stream) {
codec->audio_groups[codec->num_audio_groups++] = audioGroup;
rc = B_OK;
goto done;
}
free_afg:
free(afg);
free_audio_group:
free(audioGroup);
done:
return rc;
}
// #pragma mark -
void
hda_codec_delete(hda_codec* codec)
{
if (codec != NULL) {
if (codec == NULL) {
uint32 idx;
delete_sem(codec->response_sem);
for (idx = 0; idx < codec->num_afgs; idx++) {
hda_afg_delete(codec->afgs[idx]);
codec->afgs[idx] = NULL;
for (idx = 0; idx < codec->num_audio_groups; idx++) {
hda_codec_delete_audio_group(codec->audio_groups[idx]);
codec->audio_groups[idx] = NULL;
}
free(codec);
}
}
@@ -569,7 +589,7 @@ hda_codec_new(hda_controller* controller, uint32 cad)
hda_codec* codec = calloc(1, sizeof(hda_codec));
uint32 responses[3];
corb_t verbs[3];
uint32 nid;
uint32 nodeID;
if (codec == NULL)
goto exit_new;
@@ -587,19 +607,19 @@ hda_codec_new(hda_controller* controller, uint32 cad)
goto cmd_failed;
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;
nid < (responses[2] >> 16) + (responses[2] & 0xFF); nid++) {
for (nodeID = responses[2] >> 16;
nodeID < (responses[2] >> 16) + (responses[2] & 0xff); nodeID++) {
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)
goto cmd_failed;
if ((resp & 0xFF) == 1) {
if ((resp & 0xff) == 1) {
/* 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) {
dprintf("%s: Failed to setup new audio function group (%s)!\n",
__func__, strerror(rc));
@@ -134,4 +134,16 @@ typedef enum {
#define PID_OUTPUT_AMP_CAP 0x12
#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 */
@@ -21,8 +21,8 @@ hda_stream_delete(hda_stream* stream)
if (stream->buffer_area >= B_OK)
delete_area(stream->buffer_area);
if (stream->bdl_area >= B_OK)
delete_area(stream->bdl_area);
if (stream->buffer_descriptors_area >= B_OK)
delete_area(stream->buffer_descriptors_area);
free(stream);
}
@@ -36,19 +36,19 @@ hda_stream_new(hda_controller* controller, int type)
return NULL;
stream->buffer_area = B_ERROR;
stream->bdl_area = B_ERROR;
stream->buffer_descriptors_area = B_ERROR;
switch (type) {
case STRM_PLAYBACK:
case STREAM_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:
case STREAM_RECORD:
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->id = 2;
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)
{
if (stream->running) {
uint8 sts = OREG8(controller, stream->off, STS);
if (sts) {
cpu_status status;
uint8 status;
OREG8(controller, stream->off, STS) = sts;
status = disable_interrupts();
acquire_spinlock(&stream->lock);
if (!stream->running)
return;
stream->real_time = system_time();
stream->frames_count += stream->buffer_length;
stream->buffer_cycle = (stream->buffer_cycle + 1)
% stream->num_buffers;
status = OREG8(controller, stream->off, STS);
if (status == 0)
return;
release_spinlock(&stream->lock);
restore_interrupts(status);
OREG8(controller, stream->off, STS) = 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
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 response[2], index;
physical_entry pe;
bdl_entry_t* bdl;
bdl_entry_t* bufferDescriptors;
corb_t verb[2];
uint8* buffer;
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;
}
if (stream->bdl_area >= B_OK) {
delete_area(stream->bdl_area);
stream->bdl_area = B_ERROR;
if (stream->buffer_descriptors_area >= B_OK) {
delete_area(stream->buffer_descriptors_area);
stream->buffer_descriptors_area = B_ERROR;
}
/* Calculate size of buffer (aligned to 128 bytes) */
bufferSize = stream->sample_size * stream->num_channels
* stream->buffer_length;
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) */
alloc = bufferSize * stream->num_buffers;
alloc = (alloc + B_PAGE_SIZE - 1) & (~(B_PAGE_SIZE -1));
/* 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);
if (stream->buffer_area < B_OK)
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) */
for (index = 0; index < stream->num_buffers; index++) {
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 */
alloc = stream->num_buffers * sizeof(bdl_entry_t);
alloc = (alloc + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
stream->bdl_area = create_area("hda_bdl", (void**)&bdl,
B_ANY_KERNEL_ADDRESS, alloc, B_CONTIGUOUS, 0);
if (stream->bdl_area < B_OK) {
stream->buffer_descriptors_area = create_area("hda buffer descriptors",
(void**)&bufferDescriptors, B_ANY_KERNEL_ADDRESS, alloc,
B_CONTIGUOUS, 0);
if (stream->buffer_descriptors_area < B_OK) {
delete_area(stream->buffer_area);
return stream->bdl_area;
return stream->buffer_descriptors_area;
}
/* 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) {
delete_area(stream->buffer_area);
delete_area(stream->bdl_area);
delete_area(stream->buffer_descriptors_area);
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,
alloc, stream->num_buffers);
/* Setup BDL entries */
for (index = 0; index < stream->num_buffers; index++, bdl++) {
bdl->address = stream->buffers_pa[index];
bdl->length = bufferSize;
bdl->ioc = 1;
for (index = 0; index < stream->num_buffers; index++, bufferDescriptors++) {
bufferDescriptors->address = stream->physical_buffers[index];
bufferDescriptors->length = bufferSize;
bufferDescriptors->ioc = 1;
}
/* Configure stream registers */
wfmt = stream->num_channels -1;
switch (stream->sampleformat) {
wfmt = stream->num_channels - 1;
switch (stream->sample_format) {
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_20BIT: wfmt |= (2 << 4); stream->bps = 20; break;
case B_FMT_24BIT: wfmt |= (3 << 4); stream->bps = 24; break;
case B_FMT_32BIT: wfmt |= (4 << 4); stream->bps = 32; break;
default:
dprintf("%s: Invalid sample format: 0x%lx\n", __func__,
stream->sampleformat);
stream->sample_format);
break;
}
switch (stream->samplerate) {
switch (stream->sample_rate) {
case B_SR_8000:
wfmt |= (0 << 14) | (0 << 11) | (5 << 8);
stream->rate = 8000;
@@ -271,29 +281,32 @@ hda_stream_setup_buffers(hda_afg* afg, hda_stream* stream, const char* desc)
default:
dprintf("%s: Invalid sample rate: 0x%lx\n", __func__,
stream->samplerate);
stream->sample_rate);
break;
}
dprintf("IRA: %s: setup stream %ld: SR=%ld, SF=%ld\n", __func__, stream->id,
stream->rate, stream->bps);
OREG16(afg->codec->controller, stream->off, FMT) = wfmt;
OREG32(afg->codec->controller, stream->off, BDPL) = stream->bdl_pa;
OREG32(afg->codec->controller, stream->off, BDPU) = 0;
OREG16(afg->codec->controller, stream->off, LVI) = stream->num_buffers -1;
OREG16(audioGroup->codec->controller, stream->off, FMT) = wfmt;
OREG32(audioGroup->codec->controller, stream->off, BDPL)
= stream->physical_buffer_descriptors;
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_ */
OREG32(afg->codec->controller, stream->off, CBL) = stream->sample_size
* stream->num_channels * stream->num_buffers * stream->buffer_length;
OREG8(afg->codec->controller, stream->off, CTL0)
OREG32(audioGroup->codec->controller, stream->off, CBL)
= stream->sample_size * stream->num_channels * stream->num_buffers
* stream->buffer_length;
OREG8(audioGroup->codec->controller, stream->off, CTL0)
= 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,
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);
verb[0] = MAKE_VERB(audioGroup->codec->addr, stream->io_widget,
VID_SET_CONVFORMAT, wfmt);
verb[1] = MAKE_VERB(audioGroup->codec->addr, stream->io_widget,
VID_SET_CVTSTRCHN, stream->id << 4);
rc = hda_send_verbs(audioGroup->codec, verb, response, 2);
return rc;
}
@@ -353,7 +366,7 @@ hda_interrupt_handler(hda_controller* controller)
while (controller->rirbrp <= rirbwp) {
uint32 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];
if (resp_ex & RESP_EX_UNSOL) {
@@ -394,7 +407,7 @@ hda_interrupt_handler(hda_controller* controller)
if (intsts & ~(INTSTS_CIS | INTSTS_GIS)) {
int index;
for (index = 0; index < HDA_MAXSTREAMS; index++) {
for (index = 0; index < HDA_MAX_STREAMS; index++) {
if ((intsts & (1 << index)) != 0) {
if (controller->streams[index])
hda_stream_check_intr(controller, controller->streams[index]);
@@ -431,59 +444,59 @@ hda_hw_start(hda_controller* controller)
static status_t
hda_hw_corb_rirb_init(hda_controller* controller)
{
uint32 memsz, rirboff;
uint8 corbsz, rirbsz;
uint32 memSize, rirbOffset;
uint8 corbSize, rirbSize;
status_t rc = B_OK;
physical_entry pe;
/* Determine and set size of CORB */
corbsz = REG8(controller, CORBSIZE);
if (corbsz & CORBSIZE_CAP_256E) {
controller->corblen = 256;
corbSize = REG8(controller, CORBSIZE);
if (corbSize & CORBSIZE_CAP_256E) {
controller->corb_length = 256;
REG8(controller, CORBSIZE) = CORBSIZE_SZ_256E;
} else if (corbsz & CORBSIZE_CAP_16E) {
controller->corblen = 16;
} else if (corbSize & CORBSIZE_CAP_16E) {
controller->corb_length = 16;
REG8(controller, CORBSIZE) = CORBSIZE_SZ_16E;
} else if (corbsz & CORBSIZE_CAP_2E) {
controller->corblen = 2;
} else if (corbSize & CORBSIZE_CAP_2E) {
controller->corb_length = 2;
REG8(controller, CORBSIZE) = CORBSIZE_SZ_2E;
}
/* Determine and set size of RIRB */
rirbsz = REG8(controller, RIRBSIZE);
if (rirbsz & RIRBSIZE_CAP_256E) {
controller->rirblen = 256;
rirbSize = REG8(controller, RIRBSIZE);
if (rirbSize & RIRBSIZE_CAP_256E) {
controller->rirb_length = 256;
REG8(controller, RIRBSIZE) = RIRBSIZE_SZ_256E;
} else if (rirbsz & RIRBSIZE_CAP_16E) {
controller->rirblen = 16;
} else if (rirbSize & RIRBSIZE_CAP_16E) {
controller->rirb_length = 16;
REG8(controller, RIRBSIZE) = RIRBSIZE_SZ_16E;
} else if (rirbsz & RIRBSIZE_CAP_2E) {
controller->rirblen = 2;
} else if (rirbSize & RIRBSIZE_CAP_2E) {
controller->rirb_length = 2;
REG8(controller, RIRBSIZE) = RIRBSIZE_SZ_2E;
}
/* Determine rirb offset in memory and total size of corb+alignment+rirb */
rirboff = (controller->corblen * sizeof(corb_t) + 0x7f) & ~0x7f;
memsz = (rirboff + controller->rirblen * sizeof(rirb_t) + B_PAGE_SIZE - 1)
& ~(B_PAGE_SIZE - 1);
rirbOffset = (controller->corb_length * sizeof(corb_t) + 0x7f) & ~0x7f;
memSize = (rirbOffset + controller->rirb_length * sizeof(rirb_t)
+ B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
/* Allocate memory area */
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)
return controller->rb_area;
/* 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);
return rc;
}
/* Program CORB/RIRB for these locations */
REG32(controller, CORBLBASE) = (uint32)pe.address;
REG32(controller, RIRBLBASE) = (uint32)pe.address + rirboff;
REG32(controller, RIRBLBASE) = (uint32)pe.address + rirbOffset;
/* Reset CORB read pointer */
/* NOTE: See HDA011 for corrected procedure! */
@@ -574,13 +587,14 @@ hda_hw_init(hda_controller* controller)
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)
hda_codec_new(controller, index);
}
for (index = 0; index < HDA_MAXCODECS; index++) {
if (controller->codecs[index] && controller->codecs[index]->num_afgs) {
for (index = 0; index < HDA_MAX_CODECS; index++) {
if (controller->codecs[index]
&& controller->codecs[index]->num_audio_groups > 0) {
controller->active_codec = controller->codecs[index];
break;
}
@@ -617,7 +631,7 @@ hda_hw_stop(hda_controller* controller)
int index;
/* 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)
hda_stream_stop(controller, controller->streams[index]);
}
@@ -661,7 +675,7 @@ hda_hw_uninit(hda_controller* controller)
}
/* Now delete all codecs */
for (index = 0; index < HDA_MAXCODECS; index++) {
for (index = 0; index < HDA_MAX_CODECS; index++) {
if (controller->codecs[index] != NULL)
hda_codec_delete(controller->codecs[index]);
}
@@ -11,15 +11,30 @@
#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[] = {
{ 0, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 },
{ 1, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 },
{ 2, B_MULTI_INPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 },
{ 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 },
{ 5, B_MULTI_OUTPUT_BUS, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 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 },
{ 0, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 },
{ 1, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 },
{ 2, B_MULTI_INPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 },
{ 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 },
{ 5, B_MULTI_OUTPUT_BUS, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS,
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_24BIT:
case B_FMT_32BIT:
return 4;
case B_FMT_FLOAT:
return 8;
return 4;
default:
return -1;
@@ -46,7 +59,7 @@ format2size(uint32 format)
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_minimum = B_CURRENT_INTERFACE_VERSION;
@@ -69,8 +82,8 @@ get_description(hda_afg* afg, multi_description* data)
}
/* determine output/input rates */
data->output_rates = afg->defrates;
data->input_rates = afg->defrates;
data->output_rates = audioGroup->supported_rates;
data->input_rates = audioGroup->supported_rates;
/* force existance of 48kHz if variable rates are not supported */
if (data->output_rates == 0)
@@ -81,8 +94,8 @@ get_description(hda_afg* afg, multi_description* data)
data->max_cvsr_rate = 0;
data->min_cvsr_rate = 0;
data->output_formats = afg->deffmts;
data->input_formats = afg->deffmts;
data->output_formats = audioGroup->supported_formats;
data->input_formats = audioGroup->supported_formats;
data->lock_sources = B_MULTI_LOCK_INTERNAL;
data->timecode_sources = 0;
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
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, 1, true);
@@ -108,39 +121,41 @@ get_enabled_channels(hda_afg* afg, multi_channel_enable* data)
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->input_latency = 0;
data->timecode_kind = 0;
data->output.format = afg->playback_stream->sampleformat;
data->output.rate = afg->playback_stream->samplerate;
data->output.format = audioGroup->playback_stream->sample_format;
data->output.rate = audioGroup->playback_stream->sample_rate;
data->input.format = afg->record_stream->sampleformat;
data->input.rate = afg->record_stream->sampleformat;
data->input.format = audioGroup->record_stream->sample_format;
data->input.rate = audioGroup->record_stream->sample_format;
return B_OK;
}
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;
afg->playback_stream->samplerate = data->output.rate;
afg->playback_stream->sample_size = format2size(afg->playback_stream->sampleformat);
audioGroup->playback_stream->sample_format = data->output.format;
audioGroup->playback_stream->sample_rate = data->output.rate;
audioGroup->playback_stream->sample_size = format2size(
audioGroup->playback_stream->sample_format);
afg->record_stream->samplerate = data->input.rate;
afg->record_stream->sampleformat = data->input.format;
afg->record_stream->sample_size = format2size(afg->record_stream->sampleformat);
audioGroup->record_stream->sample_rate = data->input.rate;
audioGroup->record_stream->sample_format = data->input.format;
audioGroup->record_stream->sample_size = format2size(
audioGroup->record_stream->sample_format);
return B_OK;
}
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;
return B_OK;
@@ -148,7 +163,8 @@ list_mix_controls(hda_afg* afg, multi_mix_control_info * data)
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;
return B_OK;
@@ -156,89 +172,102 @@ list_mix_connections(hda_afg* afg, multi_mix_connection_info * data)
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;
}
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 record_sample_size = afg->record_stream->sample_size;
uint32 playback_sample_size = audioGroup->playback_stream->sample_size;
uint32 record_sample_size = audioGroup->record_stream->sample_size;
uint32 cidx, bidx;
status_t rc;
status_t status;
dprintf("%s: playback: %ld buffers, %ld channels, %ld samples\n", __func__,
data->request_playback_buffers, data->request_playback_channels, data->request_playback_buffer_size);
dprintf("%s: record: %ld buffers, %ld channels, %ld samples\n", __func__,
data->request_record_buffers, data->request_record_channels, data->request_record_buffer_size);
TRACE("playback: %ld buffers, %ld channels, %ld samples\n",
data->request_playback_buffers, data->request_playback_channels,
data->request_playback_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
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;
}
/* Determine what buffers we return given the request */
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_channels = data->request_playback_channels;
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 (cidx = 0; cidx < data->return_playback_channels; cidx++) {
data->playback_buffers[bidx][cidx].base
= afg->playback_stream->buffers[bidx]
= audioGroup->playback_stream->buffers[bidx]
+ (playback_sample_size * cidx);
data->playback_buffers[bidx][cidx].stride
= playback_sample_size * data->return_playback_channels;
}
}
data->return_record_buffers = data->request_record_buffers;
data->return_record_channels = data->request_record_channels;
data->return_record_buffer_size = data->request_record_buffer_size;
/* frames */
for (bidx = 0; bidx < data->return_record_buffers; bidx++) {
for (cidx = 0; cidx < data->return_record_channels; cidx++) {
data->record_buffers[bidx][cidx].base
= afg->record_stream->buffers[bidx] + (record_sample_size * cidx);
= audioGroup->record_stream->buffers[bidx]
+ (record_sample_size * cidx);
data->record_buffers[bidx][cidx].stride
= record_sample_size * data->return_record_channels;
}
@@ -247,20 +276,22 @@ get_buffers(hda_afg* afg, multi_buffer_list* data)
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
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;
cpu_status status;
status_t rc;
if (!afg->playback_stream->running)
hda_stream_start(afg->codec->controller, afg->playback_stream);
if (!audioGroup->playback_stream->running) {
hda_stream_start(audioGroup->codec->controller,
audioGroup->playback_stream);
}
/* do playback */
rc=acquire_sem(afg->playback_stream->buffer_ready_sem);
rc = acquire_sem(audioGroup->playback_stream->buffer_ready_sem);
if (rc != B_OK) {
dprintf("%s: Error waiting for playback buffer to finish (%s)!\n", __func__,
strerror(rc));
@@ -268,13 +299,13 @@ buffer_exchange(hda_afg* afg, multi_buffer_info* data)
}
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->played_real_time = afg->playback_stream->real_time;
data->played_frames_count = afg->playback_stream->frames_count;
data->playback_buffer_cycle = audioGroup->playback_stream->buffer_cycle;
data->played_real_time = audioGroup->playback_stream->real_time;
data->played_frames_count = audioGroup->playback_stream->frames_count;
release_spinlock(&afg->playback_stream->lock);
release_spinlock(&audioGroup->playback_stream->lock);
restore_interrupts(status);
debug_buffers_exchanged++;
@@ -285,57 +316,59 @@ buffer_exchange(hda_afg* afg, multi_buffer_info* data)
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);
// delete_sem(afg->record_stream->buffer_ready_sem);
static status_t
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;
}
status_t
multi_audio_control(void* cookie, uint32 op, void* arg, size_t len)
{
hda_codec* codec = (hda_codec*)cookie;
hda_afg* afg;
hda_audio_group* audioGroup;
/* FIXME: We should simply pass the afg into here... */
if (!codec || codec->num_afgs == 0)
/* FIXME: We should simply pass the audioGroup into here... */
if (!codec || codec->num_audio_groups == 0)
return ENODEV;
afg = codec->afgs[0];
audioGroup = codec->audio_groups[0];
switch (op) {
case B_MULTI_GET_DESCRIPTION:
return get_description(afg, arg);
return get_description(audioGroup, arg);
case B_MULTI_GET_ENABLED_CHANNELS:
return get_enabled_channels(afg, arg);
return get_enabled_channels(audioGroup, arg);
case B_MULTI_SET_ENABLED_CHANNELS:
return B_OK;
case B_MULTI_GET_GLOBAL_FORMAT:
return get_global_format(afg, arg);
return get_global_format(audioGroup, arg);
case B_MULTI_SET_GLOBAL_FORMAT:
return set_global_format(afg, arg);
return set_global_format(audioGroup, arg);
case B_MULTI_LIST_MIX_CHANNELS:
return list_mix_channels(afg, arg);
return list_mix_channels(audioGroup, arg);
case B_MULTI_LIST_MIX_CONTROLS:
return list_mix_controls(afg, arg);
return list_mix_controls(audioGroup, arg);
case B_MULTI_LIST_MIX_CONNECTIONS:
return list_mix_connections(afg, arg);
return list_mix_connections(audioGroup, arg);
case B_MULTI_GET_BUFFERS:
return get_buffers(afg, arg);
return get_buffers(audioGroup, arg);
case B_MULTI_BUFFER_EXCHANGE:
return buffer_exchange(afg, arg);
return buffer_exchange(audioGroup, arg);
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_SET_EVENT_INFO: