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